Qouson's blog Qouson's blog
首页
  • Java 基础

    • 基础
    • String
  • Java 中级

    • 网络编程
  • Java 高级

    • JVM
    • 多线程
  • Spring
  • SpringMVC
  • SpringBoot
  • MySQL
  • Redis
  • MQ
  • ZooKeeper
  • git
  • linux
  • 设计模式
  • 数据结构与算法
  • 计算机基础
  • Java相关框架
  • 分布式
  • DDD领域驱动设计
  • 系统设计
  • 杂乱无章
Java知识图谱
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

qouson

Java界的小学生
首页
  • Java 基础

    • 基础
    • String
  • Java 中级

    • 网络编程
  • Java 高级

    • JVM
    • 多线程
  • Spring
  • SpringMVC
  • SpringBoot
  • MySQL
  • Redis
  • MQ
  • ZooKeeper
  • git
  • linux
  • 设计模式
  • 数据结构与算法
  • 计算机基础
  • Java相关框架
  • 分布式
  • DDD领域驱动设计
  • 系统设计
  • 杂乱无章
Java知识图谱
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • String

  • 网络编程

  • JVM

    • JVM-ERROR
    • JVM-GC
    • JVM-体系结构
    • JVM-参数和调优
    • JVM-类运行机制
      • 运行数据区
      • 类生命周期
      • 类加载器
        • 1.Bootstrap Class Loader ---- 核心类库加载器
        • 2.Extension Class Loader ---- 扩展类库加载器
        • 3.Application Class Loader ---- 用户程序加载器
      • 验证
        • 查看类对应的加载器
        • JVM如何知道我们的类在何方
        • 类会不会重复加载
        • 类的卸载
        • 双亲委派模型
    • JVM
  • 多线程

  • JavaSE
  • JVM
qouson
2024-05-23
目录

JVM-类运行机制

# JVM-类运行机制

# 运行数据区

20210412110307

  • JVM用来存储加载的类信息、常量、静态变量、编译后的代码等数据
  • 虚拟机中这是一个逻辑划分。具体实现根据不同虚拟机来实现。如oracle的HotSpot在Java7中方法区放在永久代,Java8放在元数据空间,并且通过GC机制对这个区域进行管理。

# 类生命周期

20210412200132

  • 1.加载----读取二进制内容
  • 2.验证----验证class文件格式规范,语义分析,引用验证,字节码验证
  • 3.准备----分配内存,设置类static修饰的变量初始值
  • 4.解析----类,接口,字段,类方法等解析
  • 5.初始化----为静态变量赋值;执行静态代码块
  • 6.使用----创建实例对象
  • 7.写在----从JVM方法区中卸载

# 类加载器

  • 类加载器负责装入类,搜索网络,jar,zip,文件夹,二进制数据,内存等指定位置的类资源。一个java程序运行,最少有三个类加载器实例,负责不同类的加载。

# 1.Bootstrap Class Loader ---- 核心类库加载器

  • c/c++实现,无对应的java类:null
  • 加载JRE_HOME/jre/lib目录,或用户配置的目录,JDK的核心类库 rt.jar...String...

# 2.Extension Class Loader ---- 扩展类库加载器

  • ExtClassLoader的实例
  • 加载JRE_HOME/jre/lib/ext目录,JDK扩展包,或用户配置的目录

# 3.Application Class Loader ---- 用户程序加载器

  • AppClassLoader的实例
  • 加载java.class.path指定的目录,用户程序class-path或者java命令运行时参数-cp...

# 验证

# 查看类对应的加载器

  • 通过JDK-API查看:java.lang.Class.getClassLoader()
  • 返回装载类的加载器
  • 如果这个类是由BootstrapClassLoader加载的,那么这个方法在这种实现中将返回null
  • demo
public class ClassLoaderView {
    public static void main(String[] args) throws ClassNotFoundException {
        //加载核心类库的Bootstrap ClassLoader
        System.out.println("核心类库加载器:" + ClassLoaderView.class.getClassLoader().loadClass("java.lang.String").getClassLoader());
        //加载扩展类库的Extension ClassLoader
        System.out.println("扩展类库加载器:" + ClassLoaderView.class.getClassLoader().loadClass("com.sun.nio.zipfs.ZipCoder").getClassLoader());
        //加载应用程序的Application ClassLoader
        System.out.println("应用程序的加载器:" + ClassLoaderView.class.getClassLoader());
        //双亲委派模型 Parents Delegation Model
        System.out.println("应用程序库加载器的父类" + ClassLoaderView.class.getClassLoader().getParent());
        System.out.println("应用程序库加载器的父类的父类" + ClassLoaderView.class.getClassLoader().getParent().getParent());
        //    核心类库加载器:null
        //    扩展类库加载器:sun.misc.Launcher$ExtClassLoader@3fee733d
        //    应用程序的加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
        //    应用程序库加载器的父类sun.misc.Launcher$ExtClassLoader@3fee733d
        //    应用程序库加载器的父类的父类null
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# JVM如何知道我们的类在何方

  • 如何知道?
    • class存在在不同的位置,桌面jar,项目bin目录,target目录等
  • 查看openjdk源码:sum.misc.Luncher.AppClassLoader
  • 结论:读取java.class.path配置,指定去哪些地址加载类资源
  • 验证过程:利用jps,jcmd两个命令
    • 1.jps查看本机java进程
    • 2.查看运行时配置:jcmd 进程号VM.system_properties

# 类会不会重复加载

  • 类的唯一性:同一个类加载器,类名一样,代表是同一个类。
  • 识别方式:ClassLoader + Instance id + PackageName + ClassName
  • 验证方式:使用类加载器,对同一个class类的不同版本,进行多次加载,检查是否加载到最新的代码
  • 结论:
    • 当使用同一个类加载器加载同一个类的时候,不会重复加载
    • 当使用不同类加载器加载同一个类的时候,会重复加载,也是热加载的实现。
public class LoaderTest {
    public static void main(String[] args) throws Exception {
        URL classURL = new URL("file:" + LoaderTest.class.getClassLoader().getResource("").getFile());
        URLClassLoader loader = new URLClassLoader(new URL[]{classURL});
        while(true){
            Class clazz = loader.loadClass("com.jjc.mypratice.javase.jvm.classloader.HelloService");
            System.out.println("HelloService所使用的类加载器:" + clazz.getClassLoader());
            Object newInstance = clazz.newInstance();
            Object value = clazz.getMethod("test").invoke(newInstance);
            System.out.println("调用getValue获取的返回值为" + value);
            //每3秒执行一次
            Thread.sleep(3000L);
            System.out.println();
        }
    }
}
public class HelloService {
    public static String value = getValue();

    static{
        System.out.println("######static block");
    }

    private static String getValue(){
        System.out.println("######static method");
        return "test";
    }

    public void test(){
        System.out.println("hello2333" + value);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 类的卸载

  • 类什么时候会被卸载?
    • 1.该Class的所有实例已经被GC
    • 2.加载该类的ClassLoader的实例已经被GC
  • 验证方式:jvm启动中增加 -verbose:class 参数,输出类的加载和卸载信息

# 双亲委派模型

20210412211518

  • 为了避免重复加载,由下到上逐级委托,右上到下逐级查找。
  • 首先不会自己去尝试加载类,而是把这个请求委派给父加载器去完成;每一个层次的加载器都是如此,因此所有的类加载请求都会传递给上层的启动类加载器。
  • 只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。
  • 注:类加载器之间不存在父类子类的关系,"双亲"是翻译,可以理解为逻辑定义的上下级关系
编辑 (opens new window)
上次更新: 2024/05/24, 11:36:46
JVM-参数和调优
JVM

← JVM-参数和调优 JVM→

最近更新
01
杂乱无章
12-25
02
基础-大彬
11-14
03
集合-大彬
11-14
更多文章>
Theme by Vdoing | Copyright © 2023-2025 qouson
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式