Classloader机制
1.概述?
类加载器:负责.class文件加载到内存中,并为之生成对应的Class对象,也就是字节码对象。这样就可以使用这个类中的成员变量和方法了。而被加载到内存中的class文件就会变成class对象。
常见的类加载器有三种,每个加载器负责加载不同的位置的类:
(1)Bootstrap:根类加载器
(2)ExtClassLoader:扩展类加载器
(3)AppClassLoader:系统、应用类加载器
关于上述加载器的区别:
说明:1、Bootstrap是最顶级的加载器。加载类文件不是我们自己书写的,是JRE/lib/rt.jar包下的。只有将这个包下面的所有类加载到内存中,才可以使用这个包下的所有类。
2.ExtClassLoader扩展类加载器,是用来加载JRE/lib/ext/*.jar这个包下的所有类,在这个jar包中,都是jdk内部自己使用的。
3.AppClassLoader系统/应用类加载器。是用来加载ClassPath指定所有的jar或目录,classPath表示存放类路径。
2.委托机制
全盘负责委托机制:
ClassLoader(类加载器)加载类用的是全盘负责委托机制。
1)全盘负责:当一个ClassLoader(类加载器)加载一个类的时候,那么在这个类中所引用的所有其它的类通常也都由这个类加载器来加载。
举例:比如上述代码中我们在 ClassLoaderDemo1 类中书写如下代码:
public class ClassLoaderDemo1 { public static void main(String[] args) { // 获取当前类的加载器 ClassLoader loader = ClassLoaderDemo1.class.getClassLoader(); //输出当前类的类加载器 System.out.println(loader);//sun.misc.Launcher$AppClassLoader@b0014f0 //获取AppClassLoader类加载器的父类 ClassLoader parent = loader.getParent(); //输出AppClassLoader类加载器的父类加载器 System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@325e9e34 } }
说明:由于我们在ClassLoaderDemo1 类中使用了System类,那么System类也应该由ClassLoaderDemo1的类加载器加载到内存中。
换句话说,如果在A类中使用了B类,那么A类的加载器就会将B类也会加载到内存中,就是一个类的加载器同时把多个类都加载了。
2)委托机制:先让Parent(父)类加载器寻找,只有在Parent找不到的时候,才从自己的范围中寻找。
但是呢,全盘负责要和委托机制一起使用,一个类加载器在加载一个类的时候不是上来就先加载类,而是先咨询这个类加载器的父亲,先看他的父类加载器有没有要加载的类,如果已经存在要加载的类了,那么子类加载器就不会加载,因为在加载就会重复,产生冲突了,只有在父类加载器中找不到的时候,才从自己的范围中寻找。
举例:还是上述的代码,由于ClassLoaderDemo1 类是被 AppClassLoader 类加载器加载内存中的,那么根据全盘负责机制,AppClassLoader 类加载器也会将System类加载到内存中,但是在加载的时候,根据委托机制AppClassLoader 类加载器会先去咨询他的父亲ExtClassLoader 类加载器,而这个类加载器中也没有System类,那么又会去咨询ExtClassLoader 类加载器的父类Bootstrap类加载器,而在这个类加载器中是可以加载System类的,所以作为子类加载器AppClassLoader 就不会加载了,这样才能保证一个类只会被加载一次,任何一个类同时只会被加载一次。
如果一个类在父类加载器中找到了,那么就会把这个类加载之后保存到cache(缓存)中。
3)类加载器的cache(缓存)机制:如果cache中保存了这个类就直接返回它,如果没有才加载这个类,然后存入cache中,下一次如果有其他类在使用的时候就不会在加载了,直接去cache缓存拿即可。这就是为什么每个类只加载一次,内存只有一份的原因。
举例:还是上述代码中,当第一次使用System类的时候,那么System类就会被加载了,那么System类就会存储到内存中了,当下面代码中我们再一次使用System类的时候,由于内存中已经有了,那么就不会在去加载了,这时会直接拿过来用即可。
因此方法区中每一个类的字节码文件只有一份的原因由全盘负责、委托机制和类加载器的cache(缓存)机制共同决定。