ClassLoader与Tomcat的ClassLoader

来源:互联网 发布:cms远程监控设置 编辑:程序博客网 时间:2024/05/21 09:04

基本概念

ClassLoader

ClassLoad:类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。

双亲委派机制

什么时候需要加载类?

java虚拟机规范虽然没有强制性约束在什么时候开始类加载过程,但是对于类的初始化,虚拟机规范则严格规定了有且只有四种情况必须立即对类进行初始化,遇到new、getStatic、putStatic或invokeStatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
生成这4条指令最常见的java代码场景是:

1)使用new关键字实例化对象

2)读取一个类的静态字段(被final修饰、已在编译期把结果放在常量池的静态字段除外)

3)设置一个类的静态字段(被final修饰、已在编译期把结果放在常量池的静态字段除外)

4)调用一个类的静态方法

从程序入口开始

当我们用java命令运行一个主类的时候,jvm不得不加载这个类的class,在main开始运行后,涉及上述四种情况,关联的class都必须进行加载,以此类推。

启动jvm时可以指定一个自定义类加载器,如果没有指定,jvm将使用内置的AppClassLoader(sun.misc.Launcher.AppClassLoader)来加载主类。

委派机制

某个特定的类加载器(如AppClassLoader)在接到加载类的请求时,首先将加载任务委托给其父加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;如果父类加载器无法完成此加载任务时,最终由当前类加载器(如AppClassLoader)加载。

public class ClassLoaderTest1 {  public static void main(String[] args) {    //主类加载器    System.out.println(ClassLoaderTest1.class.getClassLoader());    //主类加载器的父类加载器    System.out.println(ClassLoaderTest1.class.getClassLoader().getParent());    //主类加载器的父类加载器的父加载器    System.out.println(ClassLoaderTest1.class.getClassLoader().getParent().getParent());  }}

输出结果:

sun.misc.Launcher$AppClassLoader@18b4aac2sun.misc.Launcher$ExtClassLoader@6f496d9fnull

一般情况下(未自定义和指定特殊的类加载器),jvm会使用AppClassLoader来加载class,AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的父加载器是null,其实不是null,是jvm的本地库加载器BootstrapClassLoader,不过因为它已经不是java编写的了,所以我们无法持有其句柄,因此打印结果为null(!important)

各自职责

AppClassLoader是一开始假设的加载器,不过根据上述委派机制,Ext和Boot在适当的时候会出手加载class,不过它们只加载特定范围内的class:

ExtClassLoader(Extension)扩展类加载器:负责加载java_home/lib/ext目录下的扩展类或 -Djava.ext.dirs 指定目录下的类。

(Bootstrap ClassLoader)启动类加载器:
负责加载java_home/jar/lib/rt.jar目录下的核心类或- Xbootclasspath指定目录下的类。由于引导类加载器全部是native代码来实现的并且涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作(打印为null)

也就是说,从主类开始遇到扩展目录下的jar包里面的类的时候,是这样一个场景

App说Ext你来,Ext说Boots你来;Boots说这不是我的菜,还是Ext你来,Ext发现这个类位于扩展路径下,当仁不让,“我来就我来”,加载该类并返回Class实例,没App啥事了。

如果遇到String这样的类,由于它是rt.jar中的class,肯定是Boots来加载了。

System.out.println(String.class.getClass().getClassLoader());// 输出null

为什么需要这样做?

为了安全!

(待续)