目录
一、JVM类加载器执行过程
JVM的类加载器分为3个阶段:
阶段一: 加载阶段
1.通过一个类的全限定名获取定义此类的二进制字节流
2.把这个字节流所代表的静态存储结构转化为方法区(元空间)的运行时数据结构
3.在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口
加载阶段实际上就是通过各种加载器把类加载到内存的方法区中,方便后续调用。
阶段二:链接阶段
链接阶段先进行验证,再进行准备,最后是解析。
验证:主要是对字节码文件的信息进行判断,主要包括文件格式验证、元数据验证、字节码验证、符号引用验证。
准备:主要是对类中的静态变量,静态代码块分配内存和赋初始值,比如private static int a=10; 这时候是定义a且给a赋值为0; 而不包括final修饰的static,final修饰的static是常量,在编译的时候就会分配好默认值,在准备阶段会进行初始化,比如 private static final int b = 10, 这时候是直接设置b=10,不会先设置为0。不会对静态代码块中的实例变量进行初始化,实例变量是会分配到堆中的。
解析:将常量池内的符号引用转换为直接引用。
阶段三:初始化阶段
初始化阶段主要是对类进行初始化赋值,执行<clinit>(),比如静态变量,静态代码块等赋初始值,比如private static int a =10; 这时候就会对a静态变量赋值为10;如果没有静态变量静态代码块,则不执行 <clinit>()方法,该方法是由jvm自动执行的;如果该类有父类,则会先执行父类的初始化<clinit>操作;比如在一个类继承了一个父类,这个父类里面有静态变量或静态代码块,则先对父类的静态变量和静态代码块进行初始化,再执行自己的初始化。
二、双亲委派机制工作原理
如果一个类加载器收到类的加载请求,它首先会把请求委托给父类的加载器,如果父类的加载器还有加载器,则继续把请求委托给父类的加载器,直到最顶层,最顶层的加载器能够完成类的加载,就会加载类,然后返回。如果父类的加载器无法加载类,则自己进行加载,直到加载成功,如果在最底层的类加载器都无法加载则加载失败。
举例子:比如String类从是先找系统类加载器(Application ClassLoader)进行加载的,但是系统类加载器的父类加载器是扩展类加载器(Extension ClassLoader),这时候就会委托扩张类加载器进行加载,扩展类加载器又会继续委托父类的加载器引导类加载器(Bootstrap ClassLoader)进行加载,引导类加载器加载了String类之后,就会返回加载成功。这时候即使扩展类加载器能够找到String类类加载,也不会进行加载。同理,如果引导类加载器加载失败,则继续判断扩展类加载器是否有String类,如果有则加载,如果加载成功,则返回,不会继续加载系统类加载器的String类。如果扩展类加载器没有String类,则加载系统类加载器的String类,如果还是加载失败,则加载失败。
好处:避免核心API被篡改,比如如果是按普通的想法如果能够加载了,就不找父的类加载器加载该类,则可以直接自己实现一个系统类加载器,来覆盖掉默认引导类加载器加载的String类,这样会出大问题的。