阅读 78

jvm之类加载机制

jvm将类的数据从class文件加载到内存中、形成可以被jvm直接使用的Class对象的过程成为类加载机制。

类的生命周期中的加载、验证、准备、解析、初始化属于类加载机制。 在这里插入图片描述

类加载的过程

1. 加载

通过类的全限定名获取二进制字节流、字节流转为方法区的运行时数据结构、在内存中生成代表这个类的java.lang.Class对象

2. 验证

确保Class文件的字节流包含的信息符合jvm的要求

3. 准备

为类中的静态变量分配内存并设置初始值

4.  解析

将常量池中的符号引用替换为直接引用

5. 初始化

真正执行编写的java程序代码,对类的静态变量进行初始化。与3中的初始化不同:3将静态变量初始化为默认值,例如整型则初始化为0;5将依据声明时指定的初始值、静态初始化块对静态变量进行初始化。

初始化的时机: 1. 创建类的实例时:通过new创建、通过反射创建、通过反序列化创建 2. 调用类的静态方法、访问或修改类的静态变量 3. 使用java.lang.reflect包的方法对类型进行反射调用时 4. 初始化某个类的子类,其所有父类都会被初始化 5. 直接使用java.exe运行某个主类,虚拟机会先初始化这个主类。 6. 当接口中包含default方法,如果这个接口的实现类进行了初始化,则这个接口要在其前被初始化

以上6种触发类初始化的方法成为主动引用。除此之外,所有引用类型的方式都不会触发初始化,称为被动引用:

  1. 通过子类引用父类的静态字段,不会导致子类初始化

  2. 通过数组定义引用类,不会触发此类的初始化

  3. 常量在编译阶段会进入类的常量池,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化

类加载器

类加载器负责将class文件加载到内存中,并为其生成对应的java.lang.Class对象。共有4类类加载器:

  1. Bootstrap ClassLoader:启动类加载器

  2. Extension ClassLoader:拓展类加载器

  3. Application ClassLoader:应用类加载器

  4. User ClassLoader: 自定义类加载器

在这里插入图片描述

类加载机制:

  1. 全盘负责

当一个类加载器负责加载某个Class时,该Class所依赖和引用的Class也由该类加载器负责,除非显式地使用另一个类加载器 2. 双亲委托
先让父加载器试图加载该Class,只有在父类加载器无法加载该Class时才尝试从自己的路径下加载 3. 缓存机制
所有加载过的类都会被缓存,当程序需要某个Class时,类加载器先从缓存中搜寻该Class,当不存在该Class时才进行加载。这也是修改Class后必须重新启动JVM,修改才生效的原因。

双亲委托的好处

  1. 沙箱安全机制:自己写的核心类和拓展类不会被加载,可以防止核心API被随意篡改

  2. 避免类的重复加载:当父亲已经加载了该类时,子类加载器不会重新加载一次

相关问题

1. new 一个对象背后发生了什么

2. 下面这段代码能运行吗?为什么

package java.lang; public class String { public static void main(String[] args) { System.out.println("string"); } } 复制代码

不能。报错如下:

java.lang.NoSuchMethodError: main Exception in thread "main" 复制代码

原因在于类加载机制中的双亲委托机制。根据双亲委托,在加载该类时会由Bootstrap ClassLoader进行加载且加载的是java核心库中的java.lang.String并非自定义的java.lang.String。核心库中的java.lang.String并没有Main方法所以会抛出NoSuchMethodError。

3. 能不能自定义一个类叫java.lang.System

看了第2题你可能会说不能,但是其实也能,通过自定义类加载器绕过双亲委托机制。


作者:junex
链接:https://juejin.cn/post/7020772793658638366


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐