JVM架构

Java应用被称为一次编译到处运行(Write Once Run Anywhere)的程序,JVM是这一特性的关键,它具有分析解释并执行字节码的能力。

JVM结构

JVM有三个主要子系统构成:ClassLoader Subsystem:类加载系统,Runtime Data Area:运行时数据区,Execution Engine:执行引擎。

类加载系统/ClassLoader Subsystem

JVM ClassLoader常见实现有三个:BootStrap ClassLoader、Extension ClassLoader、Application ClassLoader。

BootStrap ClassLoader:从引导类路径中查询类文件,目前只有rt.jar,该加载器拥有最高优先级。加载路径:JAVA_HOME/jre/lib。

扩展类加载器:从扩展目录查找类文件,默认加载路径:JAVA_HOME/jre/lib/ext,可通过系统变量java.ext.dirs调整。

应用程序类加载器:从应用程序类路径下查找类文件,类路径由环境变量java.class.path指定。

类加载过程

JVM类加载模式采用委托层次结构算法,过程分为三个阶段:Loading、Linking、Initialization。

加载:类加载器读取.class文件。

链接:执行验证、准备和解析(可选)。验证:字节码验证程序验证字节码是否正确;验证失败,抛出java.lang.VerifyError。准备:给所有静态变量分配内存并赋默认值。解析:将所有符号内存引用替换成方法区域的原始引用。

初始化:为所有静态变量分配原始值,并执行静态块。执行链从父类到子类,执行过程由上到下。

运行时数据区/Runtime Data Area

方法区(Method area):存储所有类级信息,包括静态变量。JVM共享。

堆空间(Heap area):存储所有对象及实例变量、数组。JVM内部共享。

栈区(Stack area):线程运行时堆栈。每次方法调用,都将在堆栈中新建一个条目,称为栈帧。每个帧都有自己的局部变量数组、操作数栈,以及当前方法对应类的常量池。局部变量数组和操作数栈的大小在编译时确定。局部变量数组存储局部变量。操作数堆栈存放操作数及结果,如:a+b-c+d。方法调用完成,栈帧被销毁。线程终止堆栈销毁。

PC寄存器(PC Registers):存储线程当前执行指令的地址,线程独享。

本地方法栈(Native method stacks ):存储本地方法信息,线程独享。

执行引擎/Execution Engine

分配给运行时数据区的字节码由执行引擎执行,执行引擎读取字节码并逐个执行。执行引擎由三部分构成:

解释器:逐行解释字节码。解释很快执行很慢。缺点:当一个方法被多次调用时,每次都需要解释。

实时编译器(JIT):将字节码编译成本地代码。当执行引擎检测到方法重复调用时(热点代码探测技术),启用JIT将该部分代码(最具有编译价值的代码)编译成本地代码,以此提高系统性能。中间所涉及到的组件:中间代码生成器 - 生成中间代码。代码优化器 - 负责优化生成的中间代码。目标代码生成器 - 负责生成本地代码。Profiler - 负责查找热点。

垃圾收集器:收集并销毁未被引用的对象。

Java Native Interface (JNI):JNI与本地方法库交互,并为执行引擎提供所需的本地代码库。

Native Method Libraries:本地代码库集合,为执行引擎提供所需的本地代码。

注:JIT优化代码的规则比较繁杂,不同版本不同厂商的JVM优化规则不尽相同,HotSpot VM是这方面的代表。