本文参考周志明老师的《深入理解Java虚拟机》
一.类加载的时机
加载不一定发生在什么时候,但是遇到这种情况下必须马上对类进行初始化(当然加载验证等工作要在此之前已经完成):
a.new、putstatic、getstatic、invokestatic。如果类还没有初始化就要先初始化。
b.反射。
c.对一个类初始化时,其父类没有被初始化,先初始化父类。
d.虚拟机启动时。需要加载主类。
e.JDK1.7对动态语言的支持,java.lang.invoke.MethodHandle示例最后解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,这个句柄对应的类没有被初始化,则需要先对其初始化。
这五种情况被称为主动引用,除此以外是被动引用,我们举例说明:
a.子类引用父类的静态字段,不会导致子类初始化。
b.通过数组定义类,不会引起类的初始化。
c.常量在编译阶段存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量类的初始化。
二.类加载过程
1.加载(Loading)
a.通过类的全限定名来获取此类的二进制字节流
b.将这个字节流代码所代表的静态存储结构转化成为方法区的运行时数据结构
c.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问接口。
但是对于数组类的创建过程,需要遵守以下规则:
a.非一维数组,递归采用类加载器去加载。数组类将会在再加载该组件类型(数组去掉一个维度的类型)的类加载器的类名称空间上被识别。
b.一维数组,数组类会被标记为与BootstrapClassLoader关联。
c.非一维数组可见性与其组建类型保持一致。一维数组可见性默认public。
加载阶段和链接阶段部分内容交叉进行。
2.验证
a.文件格式验证,符合Class文件规范且能被当前版本jvm识别。
b.元数据验证,保证不存在不符合java语言规范的元数据信息。
c.字节码验证,确定程序语义合法,符合逻辑。对类的方法体进行校验分析,保证被校验的类的方法在运行时不会做出为危害虚拟机安全的事情。
d.符号引用验证,对类自身以外的信息(常量池中的各种符号引用)进行匹配性校验。
3.准备阶段
为类变量分配内存并设置类变量初始值(static filed设置为0或null或等等等...)。static final 则会被直接赋值。
4.解析
是虚拟机常量池内符号引用替换为直接引用的过程。
主要针对类或接口、字段、方法、接口方法、方法类型、方法句柄和调用点7类符号引用进行解析。
5.初始化<clinit>()
a.<clinit>()由编译器自动收集类中所有类变量的赋值动作和静态语句块合成。
b.子类<clinit>()执行之前,父类<clinit>()已经执行完毕。
c.父类<clinit>()先执行意味着父类静态块限制性。
d.<clinit>()对于类或接口非必须。
e.对于接口,只有父接口定义的变量被使用,才会执行父接口的<clinit>()。
f.虚拟机保证多线程下类的<clinit>()正确。
三.类加载器
1.类与类加载器关系
类+类加载器确定类在java虚拟机中的唯一性。
我的理解:/classloader1/Test与/classloader2/Test不相同。
2.双亲委派模型(请和Tomcat类加载模型做区别)
如果一个类加载器收到了类加载请求,先把请求委托给父类,只有当父类无能为力,子加载器器才会自己尝试解决。(天赋横溢却懒惰的儿子摊上了事儿却要老子解决,老子能办的自己绝不绝不动手。)