JVM 系列(一)类加载
类加载机制是指把 class 文件加载到内存,并对数据进行校验、解析和初始化,最终形成 JVM 可以直接使用的 Java 类型的过程。
ClassLoader 加载一个 class 文件到 JVM 时需要经过以下三个的步骤:
一、加载
将 class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据构,在堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区类数据的访问入口。
二、链接
将 Java 类的二进制代码合并到 JVM 的运行状态之中的过程。
-
验证:确保加载的信息符合 JVM 规范,没有安全方面的问题。
-
准备:正式为类变量分配内存空间并设置初始值,常量的赋值也是在这一阶段进行的,这些内存都将在方法区中进行分配。
-
解析:虚拟机常量池内的符号引用替换为直接引用的过程。
三、初始化
初始化阶段是执行类构
-
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先完成其父类的初始化。
-
虚拟机会保证一个类的
() 方法在多程环境中被正确加锁和同步。 -
当访问一个 Java 关的静态域时,只有真正声明这个域的类才会被初始化。
四、主动引用和被动引用
那类在什么时候会被初始化呢?
(1) 类的主动引用(一定会发生类的初始化)
-
new 一个类的对象
-
调用类的静态成员(除了 final 常量)和静态方法
-
使同 java.lang reflect 包的方法对类进行反射调用
-
初始化子类时,要求先会初始化父类
-
当虚拟机启动时,先加载含 main() 方法在的类
(2) 类的被动引用(不会发生类的初始化)
-
当访问一个静态域时,只有真正声明这个域的类才会初始化
通过子类引用父类的静量,不会导致子类的初始化
-
通过数组定义类引用,不会触发类的初化
-
引用常量不会触发类的初始化(常量在编译阶段就存入调用类的常量池中了)
class FinalFieldClass {
public /*final*/ static String constString = "CONST";
static {
System.out.println("FinalFieldClass init");
}
}
/**
* -XX:+TraceClassLoading
*/
public class UseFinalField {
public static void main(String[] args) {
System.out.println(FinalFieldClass.constString);
}
}
constString 不使用 final 修辞时类 FinalFieldClass 被加载了:
[Loaded com.github.binarylei.jvm.UseFinalField from file:/F:/doc/java/code-2018/disruptor/target/test-classes/]
[Loaded com.github.binarylei.jvm.FinalFieldClass from file:/F:/doc/java/code-2018/disruptor/target/test-classes/]
FinalFieldClass init
CONST
constString 使用 final 修辞时类 FinalFieldClass 没有被加载:
[Loaded com.github.binarylei.jvm.UseFinalField from file:/F:/doc/java/code-2018/disruptor/target/test-classes/]
CONST
参考:
本文转载至《实战JAVA虚拟机 JVM故障诊断与性能优化》
每天用心记录一点点。内容也许不重要,但习惯很重要!