JVM-类运行机制
# JVM-类运行机制
# 运行数据区

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

- 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
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
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 参数,输出类的加载和卸载信息
# 双亲委派模型

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