JVM类加载器
类(class)加载过程
**1、Loading: **将class文件转载到内存中
**2、Linking : **
(1)Verification:校验,检测转载进来的class文件是否符合相关标准(比如校验开头的ca fa ba be).
(2)Preparation:(重要步骤),将类中的静态变量赋默认值(不是赋初值,比如类中写了一个 static int id = 8;这里会将id 赋值为 0 ;).
(3)Resolution:该class文件中所使用到的符号引用转化成直接的内存地址,即可以直接访问到。
**3、Initializing: **静态变量的赋值(赋初始值)以及调用静态代码块;
class文件被load到内存中之后,会产生两部分的内容,第一部分就是class文件被完整加载到内存里,另外一部分会创建一个class类的对象用来指向这段内存中的class文件,其他的class(类对象)需要使用的时候就通过这个class类对象来调用相应的功能。(class对象创建之后存放在 metespace 中<平常自己创建的对象都是放在堆中>)。
不同的类加载器
(1)Bootstrap------------>加载lib/rt.jar,charset.jar等核心类,Bootstrap是通过C++实现的。
//通过getClassLoader()可以去找到类加载器是什么,当输出为空时(null),说明是 Bootstrap加载的核心类
//这里的String类就是一个核心类
System.out.println(String.class.getClassLoader());//输出为 null
(2)Extension------------>加载扩展jar包jre/lib/ext/*.jar或由-Djava.ext.dirs指定
(3)App--------------->加载classpath指定内容
(4)CustomClassLoader-------------------->自定义ClassLoader
public class Test {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
//null (Bootstrap)
System.out.println(sun.net.spi.nameservice.dns.
DNSNameService.class.getClassLoader()); //sun.misc.Launcher$ExtClassLoader@7ea987ac (Extension)
^
System.out.println(Lombok.class.getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2 (App)
^
System.out.println(Test.class.getClassLoader()); //当前的这个Test类 //sun.misc.Launcher$AppClassLoader@18b4aac2 (App)
^
}
}
BootStrap类加载 加载 其他的类加载器
除了Bootstrap类加载器,另外三个加载器往深处溯源,其实也会溯源到Bootstrap类加载器,因为类加载器本身也是类,类加载器是通过Bootstrap加载出来的
public class Test {
public static void main(String[] args) {
/*ExtClassLoader加载器的加载器是Bootstrap*/
/*1.*/ System.out.println(sun.net.spi.nameservice.dns
.DNSNameService.class.getClassLoader()); //sun.misc.Launcher$ExtClassLoader@7ea987ac
/*2.*/ System.out.println(sun.net.spi.nameservice.dns
.DNSNameService.class.getClassLoader()
.getClass().getClassLoader());//null
/*AppClassLoader加载器的加载器是Bootstrap*/
/*1.*/ System.out.println(Test.class.getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2
/*2.*/ System.out.println(Test.class.getClassLoader()
.getClass().getClassLoader());//null
}
}
双亲委派机制(important):
双亲委派机制是jvm内部固定了的,无法进行修改,任何类class的加载都必须经过双亲委派进制。
双亲委派中 ‘’双亲 (父加载器)‘’ 不是指的类加载器的加载器(BootStrap),而是某个类加载器的上一级(如CustomClassLoader 的 父加载器是 Add )。
每一个加载器中有自己对应的一段存储空间(可能是list也可以是数组或者其他的)用来存储该加载器已经将哪一些类加载进去了;在加载某个(类)class文件之前,需要先通过自底向上检查该类是否已经加载进去了。
某个类的加载可能先找到了CustomClassLoader加载器,CustomClassLoader就会在自己的存储空间内查找是否有该类(class),如果发现有就直接返回不执行加载过程。如果存储空间没有找到,就向上申请查看App加载器中是否存在。
App加载器也去查找自己的存储空间看是否加载了这个类,如果发现有就直接返回不执行加载过程。如果存储空间没有找到,就向上申请查看Extension加载器中是否存在。
同理Extension加载器之后就是BootStrap加载器。
BootStrap加载器中也没有找到,那就说明这个类(class)的确是没有加载过的,这时就要加载这个类(class),首先BootStrap加载器判断这个类是否属于自己的加载范畴,如果是就由BootStrap加载,如果不是就向下给Extension加载器。
Extension加载器也判断这个类是否属于自己的加载范畴,如果是就加载,如果不是就向下给Add加载器。
Add加载器也同理进行判断,不是则向下给CustomClassLoader加载器。
CustomClassLoader也会进行判断加载,如果加载成功即返回,如果未加载成功就报class未找到异常(ClassNotFoundException)
双亲委派机制的作用?(为什么要这么繁琐的进行判断)
最重要的原因是保证class的安全,提高jvm的安全性:如果用户自定义一个类,类名与核心类库一样,假设为java.lang.String,但是用户自定义了他的加载器(CustomClassLoader加载器),不进行class类判断直接加载,那么该自定义的String类会覆盖掉核心类库的String类,开发者恶意在自定义String中加入了一些代码,执行某些程序,就有可能会导致用户很大的损失。而双亲委派机制就可以限制开发者指定String类的加载,提高了安全性。
行class类判断直接加载,那么该自定义的String类会覆盖掉核心类库的String类,开发者恶意在自定义String中加入了一些代码,执行某些程序,就有可能会导致用户很大的损失。而双亲委派机制就可以限制开发者指定String类的加载,提高了安全性。`