详解类加载机制
类加载机制
类加载是指将编译好的class文件中的字节码读入到内存,将其放在方法区并创建对应的class对象。类的加载分为加载、链接、初始化三个步骤,其中链接又分为检查、准备、解析三步。
如图:
-
加载 (将文件加载到内存的过程)
- 通过类的完全限定名查找此类的字节码文件读取字节流。
- 将这个字节流所代表的静态储存结构转化为方法区的运行时区数据结构
- 在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口。
-
验证
- 验证阶段的作用主要是为了保证Class的字节流信息符合JVM规范,不会给jvm造成危害。如果验证失败会抛出java.lang.VerifyError的异常。
- 验证阶段分为四部:
- 文件格式验证:验证字节流文件是否符合Class文件格式规范,并且能被当前虚拟机正确的处理。
- 元数据验证:对字节码描述信息进行语义分析,以保证描述的信息符合java语言规范。
- 字节码验证:主要是数据流和控制流的分析,保证被校验的方法在运行时不会危害虚拟机。
- 符号引用验证:此验证发正在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析的时候发生。
-
准备
- 准备阶段将为类中static修饰的变量分配内存(不包含类的实例变量),并设置初始化值。这里要注意的是初始值是0或null,并不是代码中设置的值(代码中设置的值在初始化阶段完成)。这里也不包含final修饰的静态变量,final修饰的静态变量在编译时就会分配。
- 例:
- private static int size = 12; // 此时size的值为0;
- final static int size = 12; // 此时size的值是12;
-
解析
- 解析主要是解析字段,接口,方法。主要将常量池中的符号引用替换为直接引用。直接引用就是直接指向目标的指针,相对偏移量等。
-
初始化
- 初始化主要完成静态块的执行,静态方法的赋值,在准备阶段,类变量已经进行了初始化,这是类加载的最后阶段,这个阶段会根据程序员通过程序制定的计划去初始化类的变量及其他资源(static块,构造函数,父类的初始化等)。此时,若被加载的父类没有被初始化,则会先进行父类初始化。
- 只有对类进行使用时才会初始化,初始化的触发条件包括创建类的实例,访问类的静态方法或静态变量。Class.forName()反射类时,后某个子类被初始化时。
其他知识点:
虚拟机是如何将字节码文件加载的内存中的哪?
答案是通过启动类加载器。启动类加载器由C++实现,因此它没有对应的java对象。所以java中只能用null来指代。除了启动类加载器外,其他的类加载器都是在java.lang.ClassLoader的子类。