类加载机制¶
七个阶段:加载、验证、准备、解析、初始化、使用、卸载。
加载¶
将字节码从各个位置(网络、磁盘等)转化为二进制字节流加载到内存中,接着会为这个类在 JVM 的方法区创建一个对应的 Class 对象,这个 Class 对象就是这个类各种数据的访问入口。
验证¶
- **JVM规范校验。**JVM 会对字节流进行文件格式校验,判断其是否符合 JVM 规范,是否能被当前版本的虚拟机处理。例如:文件是否是以
0x cafe bene开头,版本号是否在当前虚拟机处理范围之内。 - **代码逻辑校验。**JVM 会对代码组成的数据流和控制流进行校验,确保 JVM 运行该字节码文件后不会出现致命错误。例如参数类型错误、返回值错误等。
准备¶
为类变量分配内存并初始化。
- 内存分配的对象:「类变量」指的是被 static 修饰的变量,而其他所有类型的变量都属于「类成员变量」。在准备阶段,JVM 只会为「类变量」分配内存,而不会为「类成员变量」分配内存。「类成员变量」的内存分配需要等到初始化阶段才开始。
- 初始化的类型:为变量赋予 Java 语言中该数据类型的零值,而不是用户代码里初始化的值。如果一个变量是常量(被 static final 修饰)的话,那么在准备阶段就直接赋值。
解析¶
将常量池内的符号引用替换为直接引用的过程。
初始化¶
根据语句执行顺序,对类对象进行初始化。主要工作是为静态变量赋程序设定的初值。
- 使用new字节码指令创建类的实例,或者使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),或者调用一个静态方法的时候,对应类必须进行过初始化。
- 通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。
- 当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。
- 当虚拟机启动时,用户需要指定一个主类(包含main( )方法的类),虚拟机会首先初始化这个类。
- 使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。
使用¶
从入口方法开始执行用户的程序代码。
卸载¶
销毁创建的 Class 对象,最后负责运行的 JVM 退出内存。
两个区别¶
- Class.forName 除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块
- 内部实际调用的方法是
Class.forName(className,true,classloader),第2个boolean参数表示类是否需要初始化。 - Class.forName()方法实际上也是调用的CLassLoader来实现的。
- 内部实际调用的方法是
- ClassLoader 只将.class文件加载到jvm中,不会执行static中的内容,只有在 newInstance 才会去执行static块
- 实际调用的方法是
ClassLoader.loadClass(className,false),第2个 boolean参数,表示目标对象是否进行链接
- 实际调用的方法是