什么时候进行类加载
jvm虚拟机规范没有强制性的规定何时需要进行类的加载,但是如果遇到了以下几种情况的指令则强制必须立即对类进行加载
new 创建对象的时候,
getstatic 读取静态字段的时候,
putstatic 设置静态字段的时候,
invokestatic 调用类的静态方法的时候
举例: new com.test.Student();
1.虚拟机首先会去常量池定位一个类都符号引用这里比如 com.test.Student
2.没有找到对应的符号引用则执行一遍类加载过程
类加载步骤
加载 -> 连接(Linking) -> 初始化
连接细分为
验证 -> 准备 -> 解析
加载
jvm读取class文件将其字节流代表的静态存储结构转换为运行时结构,在方法区中生成以class为运行时候数据访问的入口
class文件静态存储结构内容:
- 魔数及版本号
- 常量池
- 访问标志
- 类索引,父类索引,接口索引
- 字段表
- 方法表
- 属性表
想深入了解的的可以用二进制编辑器打开 对应的class文件就能看到以上内容想深入了解的的可以用二进制编辑器打开 对应的class文件就能看到以上内容
连接(Linking)
连接又分为下面几个步骤
验证 -> 准备 -> 解析
验证
主要是确保class文件的正确性符合java虚拟机规范
准备
为类变量(static)分配内存并设置初始值(0值,或空串)
比如 static int a= 1;
准备阶段 a = 0;
解析
其中java虚拟机规范也没有强制性都要求解析发生的具体时间,意思是解析可能会在连接阶段发生,也能在其它地方发生
解析主要是是将常量池中的符号引用解析为直接引用
符号引用: 类的全限定名 比如 com.test.HelloWorld,它引用都class对象不一定已经加载到了内存中
直接引用: 能够直接或间接的定位到内存中的对象
而且虚拟机规范强制性要求了在执行以下字节码指令之前必须进行解析
anewarry、checkcast、getfield、getstatic、instanceof、invokednamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarry、new、putfield、putstatic
至于这些指令对应都都是什么意思可以去 “虚拟机字节码指令表” 中查询
初始化
在这个阶段会调用类的<clinit>
方法会对类变量,静态语句块进行赋值
它会从父类到子类依次调用<clinit>
方法,所以最先调用都肯定是java.lang.Object的<clinit>
如果是接口的话: 只有在父接口定义了变量都时候才会调用
同时如果多个线程对类同时初始化 只会有一个初始化成功,会有加锁同步的进行
到此一个类就被加载进了虚拟机,同时在方法区中以这个class为入口可以访问它的相关信息比如常量池。