标签:条件 直接 对象 pre 属性表 ini 动态 支持 end
JVM 何时、如何把 Class 文件加载到内存,形成可以直接使用的 Java 类型,并开始执行代码?
?
加载 - 连接(验证、准备、解析)- 初始化 - 使用 - 卸载。
注意,加载、验证、准备、初始化顺序是确定的,但是不是按部就班地「执行」,而是按部就班地「开始」。
另外,为了支持 Java 语言的运行时动态绑定,解析阶段有时候可以在初始化阶段之后再开始。
?
JVM 规范中没有规定什么时候加载类,但是对于初始化时机有严格规定(而加载、验证、准备必须要在初始化之前开始):
public static void main(String[] args) {
System.out.println(B.a);
/*
* 输出:
* A
* 123
*/
}
static class A {
static int a = 123;
static {
System.out.println("A");
}
}
static class B extends A {
static {
System.out.println("B");
}
}
?
包括加载、连接(验证、准备、解析)、初始化。
开发人员也可以通过自定义的类加载器来控制字节流的获取方式(即重写类加载器的 loadClass
方法)。
验证字节信息是否符合要求。
对类变量分配内存,并设置初始值(指基本数据类型的零值)。
如果是 final static 常量,则直接赋值。(JVM 如果发现 Class 文件常量池里,类字段的字段属性表中存在 ConstantValue 属性,会在准备阶段设置为 ConstantValue 属性所指定的值。比如编译期 final static 类常量值会放在 ConstantValue 里。)
把常量池里的一些符号引用替换为直接引用(内存地址)。
比如 invokestatic、invokespecial、final 方法,编译期可知,且运行期不可变的方法。
执行类的 <clinit>
。
<clinit>
方法在编译期根据类变量赋值语句、静态语句块来生成的(顺序跟源代码里定义的顺序相同)。如果源代码没有这些语句,就不会生成该方法。
JVM 会通过加锁保证类的 <clinit>
方法只会执行一次。即在多线程环境中,只能有一个线程去执行,其他线程都需要阻塞等待,直到执行完成。
标签:条件 直接 对象 pre 属性表 ini 动态 支持 end
原文地址:https://www.cnblogs.com/dmsdus/p/11469923.html