彻底剖析JVM类加载机制 (2)

image

1.1 加载class文件

字节码文件位于磁盘,当使用到某个类(例如,调用main()方法,new新对象),在磁盘中查找并通过IO读取文件的二进制流,转为方法区数据结构,并存放到方法区,在Java堆中产生 java.lang.Class对象。Class对象是可以方法区的访问入口,用于Java反射机制,获取类的各种信息。

1.2 链接过程 验证:验证class文件是不是符合规范

文件格式的验证。验证是否以0XCAFEBABE开头,版本号是否合理

元数据验证。是否有父类,是否继承了final类(final类不能被继承),非抽象类实现了所有抽象方法。

字节码验证。(略)

符号引用验证。常量池中描述类是否存在,访问的方法或字段是否存在且有足够的权限。

-Xverify:none // 取消验证 准备:为类的静态变量分配內存,初始化为系统的初始值

final static修饰的变量:直接赋值为用户定义的值,比如 private final static int value=123,直接赋值123。

private static int value=123,该阶段的值依然是0。

解析:符号引用转换成直接引用(静态链接)

Java代码中每个方法、方法参数都是符号,类加载放入方法区的常量池Constant pool中。

符号引用:应该可以理解成常量池中的这些字面量。【可能没理解对】

直接引用:符号对应代码被加载到JVM内存中的位置(指针、句柄)。

静态链接过程在类加载时完成,主要转换一些静态方法。动态链接是在程序运行期间完成的将符号引用替换为直接引用。

1.3 初始化(类初始化clinit-->初始化init)

执行< clinit>方法, clinit方法由编译器自动收集类里面的所有静态变量的赋值动作及静态语句块合并而成,也叫类构造器方法

初始化的顺序和源文件中的顺序一致

子类的< clinit>被调用前,会先调用父类的< clinit>

JVM会保证clinit方法的线程安全性

初始化时,如果实例化一个新对象,会调用<init>方法对实例变量/代码块进行初始化,并执行对应的构造方法内的代码。

类加载过程是懒加载的,用到才会加载。

初始化示例 public class JVMTest2 { static { System.out.println("JVMTest2静态块"); } { System.out.println("JVMTest2构造块"); } public JVMTest2() { System.out.println("JVMTest2构造方法"); } public static void main(String[] args) { System.out.println("main方法"); new Sub(); } } class Super { static { System.out.println("Super静态代码块"); } public Super() { System.out.println("Super构造方法"); } { System.out.println("Super普通代码块"); } } class Sub extends Super { static { System.out.println("Sub静态代码块"); } public Sub() { System.out.println("Sub构造方法"); } { System.out.println("Sub普通代码块"); } } JVMTest2静态块 main方法 Super静态代码块 Sub静态代码块 Super普通代码块 Super构造方法 Sub普通代码块 Sub构造方法

执行main方法,并不需要创建JVMTest2实例。

2 类加载器

查看当前JDK类加载器

public class PrintJDKClassLoader { public static void main(String[] args) { ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); ClassLoader parentParent = parent.getParent(); System.out.println(parentParent); } } // JDK8 sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@28a418fc null // JDK11 jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc jdk.internal.loader.ClassLoaders$PlatformClassLoader@1324409e null 2.1 类加载器(JDK8)

类加载器初始化过程:Java通过调用jvm.dll文件创建JVM,创建一个引导类加载器(由C++实现),通过JVM启动器(sun.misc.Launcher)加载扩展类加载器和应用类加载器。

启动类加载器:负责加载lib目录下的核心类库。作为JVM的一部分,由C++实现。

扩展类/平台类加载器:负责加载lib目录下的ext扩展目录中的JAR 类包。

应用程序类加载器:负责加载用户类路径ClassPath路径下的类包,主要就是加载用户自己写的类。

自定义类加载器:负责加载用户自定义路径下的类包。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgxjzg.html