一)Example: 多个类的初始化
package me;
class A {
static int a = 1;
static {
a = 2;
System.out.print("A init");
}
static int getA(){
return a;
}
}
class B extends A{
static int b = 1;
static {
b = 2;
System.out.print("B init");
}
}
class Main {
static public void main(String... args){
System.out.print(A.a);//0
System.out.print(B.a);//1
System.out.print(B.getA());//2
System.out.print(B.b);//3
}
}
在进入正文前,首先根据上面的例子试图猜测:为了Main
类中的main()
函数能够运行,JVM需要做哪些前置工作呢?这些工作是以什么顺序展开的呢?
为了更好的描述多个类的情形,可以先试图描述一个更简单的例子:把行1和行2和行3注释掉.
二)类:从0到1
一句话: 把类从介质中复制到JVM方法区,通过各种规则验证和符号解析,最后根据程序员的逻辑赋值或其他语句完成初始化
Step1.类的加载(Loading)
加载是把存储类的实体从各类介质(文件/网络/数据库/内存中实时生成等)加载到JVM的方法区中的过程,主要包含2步:
- 根据类的全限定名"me.B"通过 _ApplicationClassLoader_(或者是自己定义的ClassLoader)获得B类的二进制流
- 把流读入内存中,并转化为JVM规定的方法区结构,生成对应的
java.lang.Class
对象
现在,你可以在程序中访问到这个类~(≧▽≦)/~啦
Step2.类的连接(Linking)
JVM中的连接细分为独立的3步:验证/准备/解析
验证和准备的开始是有严格的顺序的,但是JVM可以自由选择解析发生的时机,甚至放到初始化之后
连接可以理解为把源代码转化为可执行程序的过程,当然不同于C/C++连接生成的.exe程序,JVM中的可执行程序一般是字节码(bytecode)程序
Step3.验证(Validation)
验证过程较为复杂,JVM主要验证了class文件格式/java语义限制/java程序逻辑正确性和安全性,其主要技术为静态的字节码分析,所以不能保证100%的可靠
Step4.准备(Preparation)
为me.B
类中的静态变量int b
在堆中分配内存,并设置其初始值0
Step5.解析(Resolution)
简单地说,是将符号引用转化为直接引用的过程,所有符号引用都必须转化为直接引用
符号引用: 如方法名/变量名/类等等的符号
直接引用: 直接指向一块内存区域
Step6.类的初始化(Initialization)
在初始化之前,程序已经可以在内存中访问到me.B
类和其类变量了
初始化所做的事情也很简单:JVM从上到下收集类变量赋值语句和类静态初始化块中的语句,把它放到JVM生成的<clinit>()
方法中,再执行之
对于初始化的时机,JVM有严格的规范:只有在主动引用时才会触发类的初始化
主动引用有5种情况:大致可以理解为以下几种
- 以各种方式读静态变量/写静态变量/触发静态方法时(包括出现相应的字节码指令/反射/invokedynamic)类却没有初始化
- 包含入口方法main()的类
- 初始化子类时父类还没初始化
其他情况都是被动引用
可以看到,me.B
的<clinit>()
方法中执行了下列代码:
b = 1;
b = 2;
System.out.print("B init");
三)类:从1到n再到0
类的使用(Using)
类的实例化过程,包括new
/反射等方法
类的卸载(Unloading)
当程序中无论什么方法都无法引用该类时,类从方法区被回收,大致可以理解为
- 类的所有实例被回收
- 类的ClassLoader被回收
- 没有地方引用Class对象,也无法通过反射访问
四)Example:执行过程简述
回到最初的例子,可以列出预计的执行顺序为:
- 初始化
me.Main
- 加载
me.B
- 初始化
me.A
: 输出"A init"
-
System.out.print(A.a);
:输出2 -
System.out.print(B.a);
:输出2 -
System.out.print(B.getA());
:输出2 - 初始化
me.B
:输出"B init"
-
System.out.print(B.b);
:输出2
实际执行结果验证:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。