JVM系列(二):Java类的加载机制

一、概念

1.  字节码:

a. 以前的代码(例如C++)编译后是本地机器码,不同的机器编译出来的机器码是不同的

b. Java编译后是相同的字节码文件,即存放在.class文件中的二进制文件,JVM可以执行任何符合规范的字节码文件

2. 类加载机制:将描述类的二进制数据从.class文件读入内存的不同区域中,并对数据进行校验、转换、解析和初始化,最终形成可以让JVM直接使用的Java类型

3. 类加载结果:在堆区创建了一个java.lang.Class对象,用来封装类在方法区内的数据结构

4. 允许预加载

5. 可控性强,可用自定义的类加载器完成加载

 

二、加载.class文件的方式

1. 从本地系统中直接加载

2. 通过网络下载.class文件

3. 从zip,jar等归档文件中加载.class文件

4. 从专有数据库中提取.class文件

5. 将Java源文件动态编译为.class文件

 

三、类的生命周期:加载->(验证->准备->解析)链接->初始化->使用->卸载

1. 加载,如上所述

2. 链接

a. 验证:确保被加载的类的正确性

b. 准备:为类的静态变量分配内存,而不是实例变量;并初始化为数据类型的默认值,而不是代码中赋予的值;为static final赋代码中的值

c. 解析:把类中的符号引用改为直接引用

3. 初始化:为静态变量初始化实际的值,JVM对类和类变量初始化

4. 卸载

 

四、类加载器ClassLoader:通过一个类的全限定名来获取描述此类的二进制字节流,这个动作在JVM外部实现,以便让程序自己决定如何去获取所需的类

1. 启动类加载器,BootStrap ClassLoader

a. 由C++实现,是虚拟机的一部分,其他类加载器都由Java实现,以便让程序自己决定如何去获取所需的类

b. 加载JAVA_HOME/lib目录中的类(JAVA_HOME指明JDK安装路径),或者-Xbootclasspath参数所指定的路径中的类,并且被JVM按照文件名识别的,例如JRE/lib/rt.jar

2. 扩展类加载器,Extension ClassLoader,加载JAVA_HOME/lib/ext目录下的类,或者被java.ext.dirs系统变量所指定的路径下的类。关键词:ext

3. 应用程序类加载器,Application ClassLoader

a. 程序中默认的类加载器,除非自定义了类加载器

b. 加载用户路径(ClassPath)上的类

c. 是ClassLoader中,getSystemClassLoader方法的返回值

4. 用户自定义类加载器,User ClassLoader,加载编程人员制定的目录下的类

5. 从JVM的角度来看,类加载器分为两类:BootStrap类加载器(JVM里的C++实现)、其他类加载器(外部Java实现)。

6. 从开发人员角度,分为3类:启动类加载器、扩展类加载器、应用类加载器

 

 

 

五、类与类加载器的关系

1. 类加载器虽然只用于实现类的加载动作,但它在Java程序中的作用,却远远不限于类加载阶段

2. 类本身+加载这个类的类加载器=Java虚拟机中的唯一性,用于比较类是否相等

 

六、类的加载方式

1. 命令行启动应用时,由JVM初始化加载

2. 通过Class.forName()方法动态加载:将.class文件加载到JVM中,执行类中的static块

3. 通过ClassLoader.loadClass()方法动态加载,只加载.class文件

 

七、双亲委派模型

1. 如果一个类加载器收到了类加载的请求,它首先把请求委托给父加载器去完成,依次向上,只有父加载器无法完成时,才由子加载器完成。

2. 上述过程完成后,如果AppClassLoader加载失败,会抛出ClassNotFoundException

3. 作用:解决了基础类的统一问题,防止内存中出现重复的字节码,保证Java程序安全执行。例如用户编写了java.lang.Object类,并放在ClassPath中,系统中会有多个Object类,就混乱了。

4. 类加载器间的父子关系不是继承,而是组合

5. 实现过程:即java.lang.ClassLoader中的loadClass()方法,递归调用父加载器,最终是findClass()方法完成加载操作

6. 自己写一个类加载器去加载java.lang.Object或者java.lang.String类

 

八、破坏双亲委派模型

1. 双亲委派模型不是强制要求,而是建议性的类加载机制

2. 双亲委派模型的缺点:一般情况下,基础类是被用户代码调用的;特殊情况下,基础类会调用用户的代码,例如JNDI服务

3. 线程上线文类加载器(Thread Context ClassLoader),可以通过java.lang.Thread类中的setContextClassLoader()方法设置,父加载器请求子加载器去完成加载

4. 破坏双亲委派机制的场景:

a. 涉及SPI的加载动作,例如JNDI、JDBC、JBI

b. 热拔插、热部署、模块化,增减模块时不需要重启,只需要把此模块和类加载器一起去掉

 

九、自定义类加载器

1. 应用场景:用网络传输加密的字节码

2. 使用方法:继承ClassLoader类,并重写findClass()方法

 

posted @ 2021-02-13 17:28  牧云文仔  阅读(122)  评论(0编辑  收藏  举报