1、类在什么时候被加载,初始化
触发类加载的条件:
JVM没有规定类加载的时机,但是却严格规定了五种情况下必须对类进行初始化,初始化在类加载之后,所以也可以作为类加载时机
(1)使用new,getStatic putstatic invokeStatic 这几条指令时,如果还没有被初始化,则类先要被初始化。
(2)对类进行反射调用时,如果还没有被初始化,则先要初始化
(3)一个类作为父类时,子类初始化时,先会触发父类的初始化
(4)虚拟机运行的main方法所在类,肯定要被初始化
(5)当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic、REF_putstatic、REF_inokestatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化时,触发该类初始化。
2、类是怎么被加载的
加载过程主要分为7个阶段:
加载---> 验证---->准备---> 解析---> 初始化----> 使用---->卸载
(1)加载
1)通过一个类的全限定名加载该类对应的二进制字节流,主要通过类加载器实现。
2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这类的java.lang.Class对象,作为方法区中各个类访问的入口。
(2)验证
1)文件格式验证
2)元数据验证
3)字节码验证
4)符号引用验证
(3)准备
主要是将类变量(static修饰)在方法区中分配内存并进行初始化,设为默认值(0、null、false )
(4)解析
1)类或接口解析
2)字段解析
3)类方法解析
4)接口方法解析
解析过程主要是建立符号引用和直接引用的关联关系,使得程序在运行时可以准确的访问类、字段和方法,解析过程可能因为不同的虚拟机有所不同,但是原理和任务是相似的。
符号引用:以符号形式标识被引用的名称和描述信息,而不是直接指向内存的实际对象。
直接引用:直接指向内存中对象或方法的指针,句柄或偏移量。
(5)初始化
初始化阶段会对类静态变量赋值操作,执行静态代码块和静态方法。
(6)使用
(7)卸载
3、类加载器
类加载器是通过一个类的全限定名加载这个类的二进制字节流的实现,对于任何一个类,都是由类加载器和该类的本身共同确定在虚拟机中的唯一性。
1、BootStrapClassLoader 加载核心类
2、ExtensionClassLoader 扩展类加载器
3、ApplicationClassLoader 应用类加载器,负责加载Classpath路径下的类库
4、自定义ClassLoader 通常用于实现特定逻辑(例如Replugin自定义ClassLoader来改变加载行为)
4、双亲委托模型
采用这样的方式加载类:
当类加载器收到加载类请求时,首先委托父类加载该类,所有类加载器都采用这种方式,因此所有类加载请求都会到达顶层父类,父类加载不到时再使用该类加载器中加载。这样,类加载器之间就有了一种层级关系,能够保证Java的基础类由相同的类加载器加载,对Java系统的稳定性起到至关重要的作用。