Jvm类加载器

参考链接:https://blog.csdn.net/briblue/article/details/54973413

三大类加载器与各自加载的包,执行顺行

Bootstrap ClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。

Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。

Appclass Loader 也称为SystemAppClass 加载当前应用的classpath的所有类

加载顺序:

1. Bootstrap CLassloder

2. Extention ClassLoader

3. AppClassLoader

测试Bootstrap CLassloder 加载的东西

System.out.println(System.getProperty("sun.boot.class.path"));

结果:

C:\Program Files\Java\jre1.8.0_91\lib\resources.jar;

C:\Program Files\Java\jre1.8.0_91\lib\rt.jar;

C:\Program Files\Java\jre1.8.0_91\lib\sunrsasign.jar;

C:\Program Files\Java\jre1.8.0_91\lib\jsse.jar;

C:\Program Files\Java\jre1.8.0_91\lib\jce.jar;

C:\Program Files\Java\jre1.8.0_91\lib\charsets.jar;

C:\Program Files\Java\jre1.8.0_91\lib\jfr.jar;

C:\Program Files\Java\jre1.8.0_91\classes

测试Extention ClassLoader 加载的东西

System.out.println(System.getProperty("java.ext.dirs"));

结果:

C:\Program Files\Java\jre1.8.0_91\lib\ext;C:\Windows\Sun\Java\lib\ext

测试AppClassLoader 加载的东西

System.out.println(System.getProperty("java.class.path"));

结果:

D:\workspace\ClassLoaderDemo\bin

这个路径其实就是当前java工程目录bin,里面存放的是编译生成的class文件,java工程中的bin目录一般存放的都是编译.java文件生成的二进制.class可执行文件

查看类记载器

执行代码:

public class Hello {

public static void main(String[] args) {

  ClassLoader cl = Hello.class.getClassLoader();

    System.out.println("ClassLoader is:"+cl.toString());

   }
 }

结果:

 ClassLoader is:jdk.internal.loader.ClassLoaders$[AppClassLoader@4f8e5cde](mailto:AppClassLoader@4f8e5cde)

也就是说明Hello.class文件是由AppClassLoader加载的,而像int.class,string.class是由Bootstrap ClassLoader加载的

每个类加载器都有一个父加载器(父加载器不是父类)

查看AppClassLoader的父加载器,执行代码:

   public static void main(String[] args) {

   ClassLoader cl = Hello.class.getClassLoader();

   System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());

  }

结果:

ClassLoader's parent is:jdk.internal.loader.ClassLoaders$[PlatformClassLoader@311d617d](mailto:PlatformClassLoader@311d617d)

这个说明,AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的parent是null

类图比较

image.png

Bootstrap ClassLoader是由C++编写的。

Bootstrap ClassLoader是由C++编写的。

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。然后呢,我们前面已经分析了,JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。比如ExtClassLoader。这也可以解释之前通过ExtClassLoader的getParent方法获取为Null的现象

双亲委托查找

一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托

image.png

用序列描述一下:

1. 一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。

2. 递归,重复第1部的操作。

3. 如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面,它首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径。找到就返回,没有找到,让子加载器自己去找。

4. Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找。

5. ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路径下查找。找到就返回。如果没有找到就让子类找,如果没有子类会怎么样?抛出各种异常

重要方法loadClass()

protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException

1. 执行findLoadedClass(String)去检测这个class是不是已经加载过了。

2. 执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。

3. 如果向上委托父加载器没有加载成功,则通过findClass(String)查找,前面说过ExtClassLoader的parent为null,所以它向上委托时,系统会为它指定Bootstrap ClassLoader

要注意的是如果要编写一个classLoader的子类,也就是自定义一个classloader,建议覆盖findClass()方法,而不要直接改写loadClass()方法

例如:

 if (parent != null) {

  //父加载器不为空则调用父加载器的loadClass

   c = parent.loadClass(name, false);

   } else {

   //父加载器为空则调用Bootstrap Classloader

   c = findBootstrapClassOrNull(name);

}

自定义ClassLoader

不知道大家有没有发现,不管是Bootstrap ClassLoader还是ExtClassLoader等,这些类加载器都只是加载指定的目录下的jar包或者资源。如果在某种情况下,我们需要动态加载一些东西呢?比如从D盘某个文件夹加载一个class文件,或者从网络上下载class主内容然后再进行加载,这样可以吗?如果要这样做的话,需要我们自定义一个classloader

自定义步骤

1.编写一个类继承自ClassLoader抽象类。

2.复写它的findClass()方法。

3.在findClass()方法中调用defineClass(), 这个方法在编写自定义classloader的时候非常重要,它能将class . 二进制内容转换成Class对象,如果不符合要求的会抛出各种异常

注意:一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader

可根据链接:https://blog.csdn.net/briblue/article/details/54973413查看自定义ClassLoad的例子

关键字 路径

类加载器的关联部分就是路径,也就是要加载的class或者是资源的路径。

BootStrap ClassLoader、ExtClassLoader、AppClassLoader都是加载指定路径下的jar包。如果我们要突破这种限制,实现自己某些特殊的需求,我们就得自定义ClassLoader,自已指定加载的路径,可以是磁盘、内存、网络或者其它

常见的用法是将Class文件按照某种加密手段进行加密,然后按照规则编写自定义的ClassLoader进行解密,这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性

可根据链接:https://blog.csdn.net/briblue/article/details/54973413查看例子

每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader除非子线程特别设置

总结

ClassLoader用来加载class文件的。

系统内置的ClassLoader通过双亲委托来加载指定路径下的class和资源。

可以自定义ClassLoader一般覆盖findClass()方法。

ContextClassLoader与线程相关,可以获取和设置,可以绕过双亲委托的机制

你可能感兴趣的:(Jvm类加载器)