JVM的类加载
Posted zzq6032010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM的类加载相关的知识,希望对你有一定的参考价值。
一、基本类加载机制介绍
大体引用一下《深入理解Java虚拟机》一书中对类加载的定义:虚拟机将描述类的二进制字节流(即Class文件)加载到内存中,并对其进行验证、准备、解析、初始化,最终
生成可以直接被虚拟机使用的Java类型(即已经校验合格且有clinit执行完clinit方法的Class对象),这就是JVM的类加载机制。
一个好的定义,就应该是这样准确而简练的。下面先罗列一下类的生命周期:
加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载
其中加载、验证、准备、初始化、卸载一定会严格按照上面的顺序进行。既然知道了类加载的大体流程,那么问题来了,什么时候会触发类的加载呢?
虚拟机规范中并未作出明确的要求,但是会要求有且仅有下面五种情况发生的时候,会触发类的初始化(言外之意就是初始化之前的动作必须在此之前完成),这五种情况分别为:
1、遇到new、getStatic、putStatic、invokeStatic字节码指令时;
2、通过java.lang.reflect包对类进行反射调用的时候;
3、初始化子类时,如果父类没初始化,则先初始化父类(接口除外,接口初始化规则是调用哪个接口初始化哪个,不看继承关系);
4、虚拟机启动时,会自动初始化带有main方法的类;
5、使用动态语言支持时,如果一个MethodHandle实例最后的解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄且对应的类未初始化时(恕BZ学识浅薄,目前还没
理解这种情况对应实际代码中的哪种情形,只能先放放,好在这东西一般也遇不到 (><) )。
额外需要说明两点,这两点也经常在那种仿佛是考脑筋急转弯的编程题中遇到:一是在new一个引用类数组时不会触发这个类的初始化;二是对于常量的引用不会触发类初始化(static final)。
尤其是第二点要特别说明一下,如果A类中有一个常量叫VALUE,B类中引用了这个常量,那么在编译的时候,编译器会把这个VALUE直接放入B类的运行时常量池中供其后续使用,就是说在
编译之后,A类与B类已经没有任何交互了,所以也就不会触发初始化了。
二、类加载的各个过程简介
1、加载
类加载的第一个阶段叫加载,主要做的是三步:
1)、通过类的全限定名定位到对应的class文件;
2)、将二进制字节文件转化为方法区中的运行时数据;
3)、在内存中生成一个Class对象,作为方法区中访问运行时数据(即访问2)中数据)的入口。
此阶段自由度较高,我们可以自定义一个类加载器,加载class文件
2、验证
验证主要包括四部分:文件格式校验、源数据校验(包括语义校验)、字节码校验、字符引用校验
3、准备
在此阶段,虚拟机会为类变量(即static修饰的成员变量)分配内存并赋初始的默认值。例如一个类变量private static int a = 2,在此阶段中,会给赋初值,即a=0。实际的2这个值是在
初始化阶段赋予。而如果是static final类型的变量,在此阶段会直接赋终值。
4、解析
将要加载的那个类的运行时常量池内的符号引用替换为直接引用
5、初始化
初始化的过程就是执行类构造器<clinit>()方法的过程。此方法是在编译阶段由编译器收集类中的静态变量赋值动作、静态方法、静态块的语句合并而成的。
虚拟机会保证执行<clinit>()方法前会先执行父类的<clinit>方法,同样如果是接口则不用先执行父类的类构造器。
三、类加载器
Java中一共定义了三种类加载器:启动类加载器 <-- 扩展类加载器 <-- 应用程序类加载器,继承关系如箭头所示,而我们自定的类加载器,都是以应用程序类加载器为父类。
类加载器很重要,因为JVM中定义两个类是不是同一个类,只用两个判断条件:一个是类的全限定名是否相同,另一个是加载这个类的类加载器是否相同。如果类加载器不同,
即使类的全限定名一样,虚拟机一样会判定这是两个不同的类。
Java中的类加载器加载类,采用双亲委派模型,即类加载器C要加载类A时,会先让父加载器去加载,一直往上传递,除非父类加载器中搜不到这个类,才会让子类去加载,并且
每个类只能被加载一次。这样做的好处就是可以限定核心类不会被替换篡改。比如一种常见的面试题,如果你重新编写了一个路径为java.lang.String的类,能否调用到这个自定的类
中的方法?在这里很明显答案是不会,因为JVM中已经加载了那个系统的java.lang.String类,当用应用程序类加载器加载我们自定义的String类时,由于双亲委派模型的存在,加载
任务会被委派给父类,一直往上传递,最后到启动类加载器那里,然后发现已经加载过一次这个全路径名的类了,不能重复加载,所以自定义的String类就无法被加载调用了。(有没
有发现这个剧情跟真假美猴王很相似,一直传递到佛祖那,才辨别出了真假 。。。)
类加载就暂时到这里,书中还提到一些双亲委派模型被破坏的场景,由于了解的不够透彻,就先不乱发言了。学习之路,还是要坚持死磕啊,加油!
以上是关于JVM的类加载的主要内容,如果未能解决你的问题,请参考以下文章