认识Java虚拟机中的类加载子系统和执行引擎
目录
2. 双亲委派机制(Parents Delegation Model)
一. JVM整体架构
JVM(Java虚拟机)是一套以软件方式模拟具有完整硬件系统功能,运行在一个完全隔离环境中的完整计算机系统,是物理机的软件实现。
1. 目前主流的JVM有以下几类
- Sun HotSpot VM(2010年被Oracle收购)
- BEA JRockit VM(2008年被Oracle收购)
- IBM J9 VM
- Azul VM
- Apache Harmony
- Google Dalvik VM
- Microsoft JVM
2. JVM由三个主要子系统组成
- 类加载子系统(Class Loader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
二. JVM类装载子系统
(一)类加载过程
类加载:类加载器查找class文件并加载到虚拟机内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型数据的过程。
1. 加载:从硬盘查找并通过IO读入字节码文件
2. 链接:执行验证、准备、解析步骤
- 验证:校验字节码文件的正确性
- 准备:给类的静态变量分配内存空间,并赋予默认值:Integer=0、string=NULL、Boolean=false...
- 解析:类加载器加载类所引入的其他所有类
3. 初始化:对类中静态变量初始化为指定的值,并执行静态代码块
(二)类加载器
-
启动类加载器(Bootstrap ClassLoader):使用C++实现(仅限于HotSpot),是虚拟机自身的一部分。负责JRE核心类库${JRE_HOME}/lib目录下的类包(如rt.jar、charsets.jar等。)加载到虚拟机中,其无法被Java程序直接引用。
-
扩展类加载器(Extention ClassLoader):由ExtClassLoader实现,负责加载JRE扩展目录${JRE_HOME}/lib/ext下的所有类库,开发者可以直接使用。
-
应用程序类加载器(Application ClassLoader):由APPClassLoader实现。负责加载用户类路径classpath下所指定的类库。
-
用户自定义类加载器(Custom ClassLoader):负责加载用户自定义路径下的类包,很少使用。
参考下方示例
(三)类加载机制
1. 全盘负责委托机制
当一个ClassLoader加载一个类时,除非显式地使用另一个ClassLoader,否则该类所依赖和引用的类也由这个ClassLoader载入。
2. 双亲委派机制(Parents Delegation Model)
当一个ClassLoader加载目标类时,它首先将加载请求委托给父类加载器(或上级类加载器)尝试加载,当父类加载器无法完成加载(ClassLoader在自己的搜索范围内找不到目标类)时,子类加载器才会尝试自己去加载目标类。
3. 双亲委派机制的优势
- 沙箱安全机制:我们自定义的String.class类不会被加载,防止了核心类库被篡改。
- 避免类的重复加载:当父类加载器加载了目标类后,就没必要让子类加载器再重复加载同一个类。
- JVM对类的加载方式是按需加载,也就是在运行期间动态加载,验证的方式见下方示例,注意在启动时加载参数:-verbose:class。
三. JVM执行引擎
执行引擎:读取运行时数据区的Java字节码并逐个执行。
(一)时编译器(JIT编译器)
Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会把这些代码认定为“热点代码”(Hot Spot Code)。
为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器成为即时编译器(Just In Time Compiler,JIT编译器)。
(二)解释器和编译器
许多主流的商用虚拟机,都同时包含解释器和编译器。
- 当程序需要快速启动和执行时,解释器首先发挥作用,省去编译的时间,立即执行。
- 当程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,可以提高执行效率。
如果内存资源限制较大(部分嵌入式系统),可以使用解释执行节约内存,反之可以使用编译执行来提升效率。同时编译器的代码还能退回成解释器的代码。