《Java虚拟机原理图解》3. JVM类加载器机制与类加载过程
2017-12-14 16:44
405 查看
前言
读完本文,你将了解到:
一、为什么说Jabalpur语言是跨平台的
二、Java虚拟机启动、加载类过程分析
三、类加载器有哪些?其组织结构是怎样的?
四、双亲加载模型的逻辑和底层代码实现是怎样的?
五、类加载器与Class 实例的关系
六、线程上下文加载器
一、为什么说Java语言是跨平台的?
Java语言之所以说它是跨平台的、可以在当前绝大部分的操作系统平台下运行,是因为Java语言的运行环境是在Java虚拟机中。
Java虚拟机消除了各个平台之间的差异,只要操作系统平台下安装了Java虚拟机,那么使用Java开发的东西都能在其上面运行。如下图所示:
![](https://img-blog.csdn.net/20171214151551634?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
Java虚拟机对各个平台而言,实质上是各个平台上的一个可执行程序。例如在windows平台下,java虚拟机对于windows而言,就是一个java.exe进程而已。
二、Java虚拟机启动、加载类过程分析
下面我将定义一个非常简单的java程序并运行它,来逐步分析java虚拟机启动的过程。
在windows命令行下输入:
java org.luanlouis.jvm.load.Main
当输入上述的命令时:
windows开始运行{JRE_HOME}/bin/java.exe程序,java.exe 程序将完成以下步骤:
根据JVM内存配置要求,为JVM申请特定大小的内存空间;
创建一个引导类加载器实例,初步加载系统类到内存方法区区域中;
创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader;
使用上述获取的ClassLoader实例加载我们定义的 org.luanlouis.jvm.load.Main类;
加载完成时候JVM会执行Main类的main方法入口,执行Main类的main方法;
结束,java程序运行结束,JVM销毁。
Step 1.根据JVM内存配置要求,为JVM申请特定大小的内存空间
为了降低本文的理解难度,这里就不详细介绍JVM内存配置要求的话题,今概括地介绍一下内存的功能划分。
JVM启动时,按功能划分,其内存应该由以下几部分组成:
![](https://img-blog.csdn.net/20171214152907179?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
如上图所示,JVM内存按照功能上的划分,可以粗略地划分为方法区(Method Area) 和堆(Heap),而所有的类的定义信息都会被加载到方法区中。
Step 2. 创建一个引导类加载器实例,初步加载系统类到内存方法区区域中;
JVM申请好内存空间后,JVM会创建一个引导类加载器(Bootstrap Classloader)实例,引导类加载器是使用C++语言实现的,负责加载JVM虚拟机运行时所需的基本系统级别的类,如java.lang.String, java.lang.Object等等。
引导类加载器(Bootstrap Classloader)会读取 {JRE_HOME}/lib 下的jar包和配置,然后将这些系统类加载到方法区内。
本例中,引导类加载器是用 {JRE_HOME}/lib加载类的,不过,你也可以使用参数 -Xbootclasspath 或 系统变量sun.boot.class.path来指定的目录来加载类。
一般而言,{JRE_HOME}/lib下存放着JVM正常工作所需要的系统类,如下表所示:
文件名 描述
rt.jar 运行环境包,rt即runtime,J2SE 的类定义都在这个包内
charsets.jar 字符集支持包
jce.jar 一组包,它们提供用于加密、密钥生成和协商以及 Message Authentication Code(MAC)算法的框架和实现
jsse.jar 安全套接字拓展包Java(TM) Secure Socket Extension
classlist 该文件内表示是引导类加载器应该加载的类的清单
net.properties JVM 网络配置信息
引导类加载器(Bootstrap ClassLoader) 加载系统类后,JVM内存会呈现如下格局:
![](https://img-blog.csdn.net/20171214153704535?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
当我们在代码中尝试获取系统类如java.lang.Object的类加载器时,你会始终得到NULL:
Step 3. 创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader
上述步骤完成,JVM基本运行环境就准备就绪了。接着,我们要让JVM工作起来了:运行我们定义的程序 org.luanlouis,jvm.load.Main。
此时,JVM虚拟机调用已经加载在方法区的类sun.misc.Launcher 的静态方法getLauncher(), 获取sun.misc.Launcher 实例:
sun.misc.Launcher 使用了单例模式设计,保证一个JVM虚拟机内只有一个sun.misc.Launcher实例。
在Launcher的内部,其定义了两个类加载器(ClassLoader),分别是sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader,这两个类加载器分别被称为拓展类加载器(Extension ClassLoader) 和 应用类加载器(Application ClassLoader).如下图所示:
![](https://img-blog.csdn.net/20171214154226920?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
图例注释:除了引导类加载器(Bootstrap Class Loader )的所有类加载器,都有一个能力,就是判断某一个类是否被引导类加载器加载过,如果加载过,可以直接返回对应的Class instance,如果没有,则返回null. 图上的指向引导类加载器的虚线表示类加载器的这个有限的访问 引导类加载器的功能。
此时的 launcher.getClassLoader() 方法将会返回 AppClassLoader 实例,AppClassLoader将ExtClassLoader作为自己的父加载器。
当AppClassLoader加载类时,会首先尝试让父加载器ExtClassLoader进行加载,如果父加载器ExtClassLoader加载成功,则AppClassLoader直接返回父加载器ExtClassLoader加载的结果;如果父加载器ExtClassLoader加载失败,AppClassLoader则会判断该类是否是引导的系统类(即是否是通过Bootstrap类加载器加载,这
f472
会调用Native方法进行查找);若要加载的类不是系统引导类,那么ClassLoader将会尝试自己加载,加载失败将会抛出“ClassNotFoundException”。
具体AppClassLoader的工作流程如下所示:
![](https://img-blog.csdn.net/20171214154654623?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
双亲委派模型(parent-delegation model)
上面讨论的应用类加载器AppClassLoader的加载类的模式就是我们常说的双亲委派模型(parent-delegation model).
对于某个特定的类加载器而言,应该为其指定一个父类加载器,当用其进行加载类的时候:
委托父类加载器帮忙加载;
父类加载器加载不了,则查询引导类加载器有没有加载过该类;
如果引导类加载器没有加载过该类,则当前的类加载器应该自己加载该类;
若加载成功,返回 对应的Class 对象;若失败,抛出异常“ClassNotFoundException”。
请注意:
双亲委派模型中的”双亲”并不是指它有两个父类加载器的意思,一个类加载器只应该有一个父加载器。上面的步骤中,有两个角色:
1. 父类加载器(parent classloader):它可以替子加载器尝试加载类
2. 引导类加载器(bootstrap classloader): 子类加载器只能判断某个类是否被引导类加载器加载过,而不能委托它加载某个类;换句话说,就是子类加载器不能接触到引导类加载器,引导类加载器对其他类加载器而言是透明的。
一般情况下,双亲加载模型如下所示:
![](https://img-blog.csdn.net/20171214160251019?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
Step 4. 使用类加载器ClassLoader加载Main类
通过 launcher.getClassLoader()方法返回AppClassLoader实例,接着就是AppClassLoader加载 org.luanlouis.jvm.load.Main类的时候了。
上述定义的org.luanlouis.jvm.load.Main类被编译成org.luanlouis.jvm.load.Main class二进制文件,这个class文件中有一个叫常量池(Constant Pool)的结构体来存储该class的常亮信息。常量池中有CONSTANT_CLASS_INFO类型的常量,表示该class中声明了要用到那些类:
![](https://img-blog.csdn.net/20171214160544430?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
当AppClassLoader要加载 org.luanlouis.jvm.load.Main类时,会去查看该类的定义,发现它内部声明使用了其它的类: sun.security.pkcs11.P11Util、java.lang.Object、java.lang.System、java.io.PrintStream、java.lang.Class;org.luanlouis.jvm.load.Main类要想正常工作,首先要能够保证这些其内部声明的类加载成功。所以AppClassLoader要先将这些类加载到内存中。(注:为了理解方便,这里没有考虑懒加载的情况,事实上的JVM加载类过程比这复杂的多)
加载顺序:
1. 加载java.lang.Object、java.lang.System、java.io.PrintStream、java.lang.Class
AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现不是其加载范围,其返回null;AppClassLoader发现父类加载器ExtClassLoader无法加载,则会查询这些类是否已经被BootstrapClassLoader加载过,结果表明这些类已经被BootstrapClassLoader加载过,则无需重复加载,直接返回对应的Class实例;
2. 加载sun.security.pkcs11.P11Util
此在{JRE_HOME}/lib/ext/sunpkcs11.jar包内,属于ExtClassLoader负责加载的范畴。AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现其正好属于加载范围,故ExtClassLoader负责将其加载到内存中。ExtClassLoader在加载sun.security.pkcs11.P11Util时也分析这个类内都使用了哪些类,并将这些类先加载内存后,才开始加载sun.security.pkcs11.P11Util,加载成功后直接返回对应的Class
相对应地,我们可以整理出双亲模型的工作流程图:
![](https://img-blog.csdn.net/20171214163729204?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
相信读者看过这张图后会对双亲加载模型有了非常清晰的脉络。当然,这是JDK自身默认的加载类的行为,我们可以通过继承复写该方法,改变其行为。
五、类加载器与Class 实例的关系
![](https://img-blog.csdn.net/20171214164011555?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
六、线程上下文加载器
Java 任何一段代码的执行,都有对应的线程上下文。如果我们在代码中,想看当前是哪一个线程在执行当前代码,我们经常是使用如下方法:
![](https://img-blog.csdn.net/20171214164152833?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VueXVodWFfa2V5Ym9hcmQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
相应地,我们可以为当前的线程指定类加载器。在上述的例子中, 当执行 java org.luanlouis.jvm.load.Main 的时候,JVM会创建一个Main线程,而创建应用类加载器AppClassLoader的时候,会将AppClassLoader 设置成Main线程的上下文类加载器:
线程上下文类加载器是从线程的角度来看待类的加载,为每一个线程绑定一个类加载器,可以将类的加载从单纯的 双亲加载模型解放出来,进而实现特定的加载需求。
原文地址:http://blog.csdn.net/luanlouis/article/details/50529868
读完本文,你将了解到:
一、为什么说Jabalpur语言是跨平台的
二、Java虚拟机启动、加载类过程分析
三、类加载器有哪些?其组织结构是怎样的?
四、双亲加载模型的逻辑和底层代码实现是怎样的?
五、类加载器与Class 实例的关系
六、线程上下文加载器
一、为什么说Java语言是跨平台的?
Java语言之所以说它是跨平台的、可以在当前绝大部分的操作系统平台下运行,是因为Java语言的运行环境是在Java虚拟机中。
Java虚拟机消除了各个平台之间的差异,只要操作系统平台下安装了Java虚拟机,那么使用Java开发的东西都能在其上面运行。如下图所示:
Java虚拟机对各个平台而言,实质上是各个平台上的一个可执行程序。例如在windows平台下,java虚拟机对于windows而言,就是一个java.exe进程而已。
二、Java虚拟机启动、加载类过程分析
下面我将定义一个非常简单的java程序并运行它,来逐步分析java虚拟机启动的过程。
package org.luanlouis.jvm.load; import sun.security.pkcs11.P11Util; /** * Created by louis on 2016/1/16. */ public class Main{ public static void main(String[] args) { System.out.println("Hello,World!"); ClassLoader loader = P11Util.class.getClassLoader(); System.out.println(loader); } }
在windows命令行下输入:
java org.luanlouis.jvm.load.Main
当输入上述的命令时:
windows开始运行{JRE_HOME}/bin/java.exe程序,java.exe 程序将完成以下步骤:
根据JVM内存配置要求,为JVM申请特定大小的内存空间;
创建一个引导类加载器实例,初步加载系统类到内存方法区区域中;
创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader;
使用上述获取的ClassLoader实例加载我们定义的 org.luanlouis.jvm.load.Main类;
加载完成时候JVM会执行Main类的main方法入口,执行Main类的main方法;
结束,java程序运行结束,JVM销毁。
Step 1.根据JVM内存配置要求,为JVM申请特定大小的内存空间
为了降低本文的理解难度,这里就不详细介绍JVM内存配置要求的话题,今概括地介绍一下内存的功能划分。
JVM启动时,按功能划分,其内存应该由以下几部分组成:
如上图所示,JVM内存按照功能上的划分,可以粗略地划分为方法区(Method Area) 和堆(Heap),而所有的类的定义信息都会被加载到方法区中。
Step 2. 创建一个引导类加载器实例,初步加载系统类到内存方法区区域中;
JVM申请好内存空间后,JVM会创建一个引导类加载器(Bootstrap Classloader)实例,引导类加载器是使用C++语言实现的,负责加载JVM虚拟机运行时所需的基本系统级别的类,如java.lang.String, java.lang.Object等等。
引导类加载器(Bootstrap Classloader)会读取 {JRE_HOME}/lib 下的jar包和配置,然后将这些系统类加载到方法区内。
本例中,引导类加载器是用 {JRE_HOME}/lib加载类的,不过,你也可以使用参数 -Xbootclasspath 或 系统变量sun.boot.class.path来指定的目录来加载类。
一般而言,{JRE_HOME}/lib下存放着JVM正常工作所需要的系统类,如下表所示:
文件名 描述
rt.jar 运行环境包,rt即runtime,J2SE 的类定义都在这个包内
charsets.jar 字符集支持包
jce.jar 一组包,它们提供用于加密、密钥生成和协商以及 Message Authentication Code(MAC)算法的框架和实现
jsse.jar 安全套接字拓展包Java(TM) Secure Socket Extension
classlist 该文件内表示是引导类加载器应该加载的类的清单
net.properties JVM 网络配置信息
引导类加载器(Bootstrap ClassLoader) 加载系统类后,JVM内存会呈现如下格局:
引导类加载器将类信息加载到方法区中,以特定方式组织,对于某一个特定的类而言,在方法区中它应该有 运行时常量池、类型信息、字段信息、方法信息、类加载器的引用,对应class实例的引用等信息。 类加载器的引用,由于这些类是由引导类加载器(Bootstrap Classloader)进行加载的,而 引导类加载器是有C++语言实现的,所以是无法访问的,故而该引用为NULL 对应class实例的引用, 类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点。
当我们在代码中尝试获取系统类如java.lang.Object的类加载器时,你会始终得到NULL:
System.out.println(String.class.getClassLoader());//null System.out.println(Object.class.getClassLoader());//null System.out.println(Math.class.getClassLoader());//null System.out.println(System.class.getClassLoader());//null
Step 3. 创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader
上述步骤完成,JVM基本运行环境就准备就绪了。接着,我们要让JVM工作起来了:运行我们定义的程序 org.luanlouis,jvm.load.Main。
此时,JVM虚拟机调用已经加载在方法区的类sun.misc.Launcher 的静态方法getLauncher(), 获取sun.misc.Launcher 实例:
sun.misc.Launcher launcher = sun.misc.Launcher.getLauncher(); //获取Java启动器 ClassLoader classLoader = launcher.getClassLoader(); //获取类加载器ClassLoader用来加载class到内存来
sun.misc.Launcher 使用了单例模式设计,保证一个JVM虚拟机内只有一个sun.misc.Launcher实例。
在Launcher的内部,其定义了两个类加载器(ClassLoader),分别是sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader,这两个类加载器分别被称为拓展类加载器(Extension ClassLoader) 和 应用类加载器(Application ClassLoader).如下图所示:
图例注释:除了引导类加载器(Bootstrap Class Loader )的所有类加载器,都有一个能力,就是判断某一个类是否被引导类加载器加载过,如果加载过,可以直接返回对应的Class instance,如果没有,则返回null. 图上的指向引导类加载器的虚线表示类加载器的这个有限的访问 引导类加载器的功能。
此时的 launcher.getClassLoader() 方法将会返回 AppClassLoader 实例,AppClassLoader将ExtClassLoader作为自己的父加载器。
当AppClassLoader加载类时,会首先尝试让父加载器ExtClassLoader进行加载,如果父加载器ExtClassLoader加载成功,则AppClassLoader直接返回父加载器ExtClassLoader加载的结果;如果父加载器ExtClassLoader加载失败,AppClassLoader则会判断该类是否是引导的系统类(即是否是通过Bootstrap类加载器加载,这
f472
会调用Native方法进行查找);若要加载的类不是系统引导类,那么ClassLoader将会尝试自己加载,加载失败将会抛出“ClassNotFoundException”。
具体AppClassLoader的工作流程如下所示:
双亲委派模型(parent-delegation model)
上面讨论的应用类加载器AppClassLoader的加载类的模式就是我们常说的双亲委派模型(parent-delegation model).
对于某个特定的类加载器而言,应该为其指定一个父类加载器,当用其进行加载类的时候:
委托父类加载器帮忙加载;
父类加载器加载不了,则查询引导类加载器有没有加载过该类;
如果引导类加载器没有加载过该类,则当前的类加载器应该自己加载该类;
若加载成功,返回 对应的Class 对象;若失败,抛出异常“ClassNotFoundException”。
请注意:
双亲委派模型中的”双亲”并不是指它有两个父类加载器的意思,一个类加载器只应该有一个父加载器。上面的步骤中,有两个角色:
1. 父类加载器(parent classloader):它可以替子加载器尝试加载类
2. 引导类加载器(bootstrap classloader): 子类加载器只能判断某个类是否被引导类加载器加载过,而不能委托它加载某个类;换句话说,就是子类加载器不能接触到引导类加载器,引导类加载器对其他类加载器而言是透明的。
一般情况下,双亲加载模型如下所示:
Step 4. 使用类加载器ClassLoader加载Main类
通过 launcher.getClassLoader()方法返回AppClassLoader实例,接着就是AppClassLoader加载 org.luanlouis.jvm.load.Main类的时候了。
ClassLoader classloader = launcher.getClassLoader();//取得AppClassLoader类 classLoader.loadClass("org.luanlouis.jvm.load.Main");//加载自定义类
上述定义的org.luanlouis.jvm.load.Main类被编译成org.luanlouis.jvm.load.Main class二进制文件,这个class文件中有一个叫常量池(Constant Pool)的结构体来存储该class的常亮信息。常量池中有CONSTANT_CLASS_INFO类型的常量,表示该class中声明了要用到那些类:
当AppClassLoader要加载 org.luanlouis.jvm.load.Main类时,会去查看该类的定义,发现它内部声明使用了其它的类: sun.security.pkcs11.P11Util、java.lang.Object、java.lang.System、java.io.PrintStream、java.lang.Class;org.luanlouis.jvm.load.Main类要想正常工作,首先要能够保证这些其内部声明的类加载成功。所以AppClassLoader要先将这些类加载到内存中。(注:为了理解方便,这里没有考虑懒加载的情况,事实上的JVM加载类过程比这复杂的多)
加载顺序:
1. 加载java.lang.Object、java.lang.System、java.io.PrintStream、java.lang.Class
AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现不是其加载范围,其返回null;AppClassLoader发现父类加载器ExtClassLoader无法加载,则会查询这些类是否已经被BootstrapClassLoader加载过,结果表明这些类已经被BootstrapClassLoader加载过,则无需重复加载,直接返回对应的Class实例;
2. 加载sun.security.pkcs11.P11Util
此在{JRE_HOME}/lib/ext/sunpkcs11.jar包内,属于ExtClassLoader负责加载的范畴。AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现其正好属于加载范围,故ExtClassLoader负责将其加载到内存中。ExtClassLoader在加载sun.security.pkcs11.P11Util时也分析这个类内都使用了哪些类,并将这些类先加载内存后,才开始加载sun.security.pkcs11.P11Util,加载成功后直接返回对应的Class
//提供class类的二进制名称表示,加载对应class,加载成功,则返回表示该类对应的Class<T> instance 实例 public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,检查是否已经被当前的类加载器记载过了,如果已经被加载,直接返回对应的Class<T>实例 Class<?> c = findLoadedClass(name); //初次加载 if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { //如果有父类加载器,则先让父类加载器加载 c = parent.loadClass(name, false); } else { // 没有父加载器,则查看是否已经被引导类加载器加载,有则直接返回 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } // 父加载器加载失败,并且没有被引导类加载器加载,则尝试该类加载器自己尝试加载 if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); // 自己尝试加载 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } //是否解析类 if (resolve) { resolveClass(c); } return c; } }
相对应地,我们可以整理出双亲模型的工作流程图:
相信读者看过这张图后会对双亲加载模型有了非常清晰的脉络。当然,这是JDK自身默认的加载类的行为,我们可以通过继承复写该方法,改变其行为。
五、类加载器与Class 实例的关系
六、线程上下文加载器
Java 任何一段代码的执行,都有对应的线程上下文。如果我们在代码中,想看当前是哪一个线程在执行当前代码,我们经常是使用如下方法:
Thread thread = Thread.currentThread();//返回对当当前运行线程的引用
相应地,我们可以为当前的线程指定类加载器。在上述的例子中, 当执行 java org.luanlouis.jvm.load.Main 的时候,JVM会创建一个Main线程,而创建应用类加载器AppClassLoader的时候,会将AppClassLoader 设置成Main线程的上下文类加载器:
public Launcher() { Launcher.ExtClassLoader var1; try { var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } //将AppClassLoader设置成当前线程的上下文加载器 Thread.currentThread().setContextClassLoader(this.loader); //....... }
线程上下文类加载器是从线程的角度来看待类的加载,为每一个线程绑定一个类加载器,可以将类的加载从单纯的 双亲加载模型解放出来,进而实现特定的加载需求。
原文地址:http://blog.csdn.net/luanlouis/article/details/50529868
相关文章推荐
- 《Java虚拟机原理图解》5. JVM类加载器机制与类加载过程
- 《Java虚拟机原理图解》5. JVM类加载器机制与类加载过程
- 《Java虚拟机原理图解》 JVM类加载器机制与类加载过程
- 5. JVM类加载器机制与类加载过程
- JVM(三):类加载机制(类加载过程和类加载器)
- 类加载机制(类加载过程和类加载器)
- JVM(三):类加载机制(类加载过程和类加载器)
- JVM(三):类加载机制(类加载过程和类加载器)
- 【转】JVM 类加载机制(类加载过程和类加载器)
- 5. JVM类加载器机制与类加载过程
- JVM(三):类加载机制(类加载过程和类加载器)
- (二十七)JVM类加载器机制与类加载过程
- 深入理解Java虚拟机——JVM类加载机制(类加载过程和类加载器)
- JVM类加载机制(类加载过程和类加载器)
- 类加载机制(类加载过程和类加载器)
- 图解JVM类加载机制与类加载过程
- java虚拟机类加载机制、类加载器、自定义类加载器
- JVM类加载机制详解(一)JVM类加载过程
- ionic 3.0懒加载机制 实现过程
- 加载plist过程中用到哪些缓存机制