大家好,我是骄阳
今天我们来聊一下jvm类加载过程。
之前我都是直接背这个面试问题的八股文,然后面试的时候装作不是提前背好的,循序渐进地说出来,但我发现,背完很容易忘,而且我根本禁不住面试官问,他随便延伸一点我就答不上来
主要原因还是因为我没有理解它。
那么首先什么是类加载?
其实就是从我们写出来的.java文件到被程序使用的整个过程
总共分为七步
package mysqltest;
public class Math {
public static final int initData = 666;
public static final Object obj = new Object();
public int compute() {
//一个方法对应一块栈帧内存区域
int a = 1; int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = new Math();
math.compute();
} }
1 加载:
定义:
在硬盘上查找class文件,并通过IO流读取字节码文件。
注意:
jvm是懒加载,所以只有使用到类时才会加载,例如调用类的main()方法,new对象等等 ,主类在运行过程中如果使用到其它类,会逐步加载这些类。在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为这个类的各种数据的访问入口
为什么是懒加载的?
其实也很容易想明白的,jar包或war包里的类可能上万个,肯定不是一次性全部加载进来,不然启动太慢了。
2 验证:
校验字节码文件的正确性
比如开头是否是cafe babe,这是约定俗成的。
一个class文件的16进制大体结构如下图:
对应的含义如下,细节可以查下oracle官方文档
3 准备:
给类的静态变量分配内存,并赋予默认值,比如int类型的值默认为0
4 解析:
将符号引用替换为直接引用。
那么什么是符号引用?
拓展:字面量(Literal)和符号引用(Symbolic References)
Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic References)。
一个class文件的16进制大体结构如下图:
当然我们一般不会去人工解析这种16进制的字节码文件,我们一般可以通过javap命令生成更可读的JVM字节码指令文件:
javap -v Math.class
红色标出的就是class常量池信息,常量池中主要存放两大类常量:字面量和符号引用。
字面量
字面量就是指由字母、数字等构成的字符串或者数值常量
字面量只可以右值出现,所谓右值是指等号右边的值。
如:int a=1 这里的a为左值,1为右值。在这个例子中1就是字面量。
int a = 1; int b = 2; int c = "abcdefg"; int d = "abcdefg";
符号引用
符号引用是编译原理中的概念,是相对于直接引用来说的。主要包括了以下三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
上面的a,b就是字段名称,就是一种符号引用,还有Math类的全限定名,main和compute是方法名称,()是一种UTF8格式的描述符,这些都是符号引用。
刚才我们讲清楚了字面量和符号引用,那么什么是直接引用?将符号引用替换为直接引用,又是什么样的过程?
简单来说就是,obj这个符号引用在运行时就会被转变为obj具体代码在内存中的地址
我们来详细说下
我们知道我们写的每一行代码最终都要加载到内存里,那么内存那么大,这个obj 变量到底放在哪里,是不是会有一个内存地址来标识,如果我们用一个指针指向这个内存地址,这个指针就是直接引用。
等我们需要用到这个变量的时候,就可以直接通过指针指向的地址找到。
而我们在加载类的时候,解析 obj这段代码并指向内存某个地址,然后将符号引用 obj和这个内存地址进行映射的过程,就是解析这个步骤要做的事,也叫做符号引用转换为直接引用。
整个过程主要通过对象头里的类型指针去转换直接引用。
同时这个过程,也就是我们通常说的静态链接。
既然提到了静态链接,那么和它成对出现的动态链接又是什么?
其实本质都是一样的
只不过动态链接是类加载期间不进行指向,程序运行期间再指向。
和静态链接的区别就是进行指向的时间不同。
5 初始化:
(1)对类的静态变量初始化为指定的值
int initData = 666
(2)执行静态代码块
6 使用
7 卸载
好啦,这就是今天全部的内容,喜欢我的话,可以点个关注支持一下~