虚拟机类加载机制(3)——线程上下文类加载器

之所以将线程上下文类加载器(Thread Context ClassLoader)单独拿出来写,确实是因为它涉及的东西比较多,既然带有线程两个字,一定也是非常重要的一个东西。

我们首先来回顾一下类加载器的双亲委派模型。

在上一章《虚拟机类加载机制(2)——类加载器》中我们解释了何为类加载器的“双亲委派模型”,知道了双亲委派模型给我们带了一个好处就是Java类随着它的类一起具备了一种带有优先级的层次关系。简单的例子就是Object类在程序的各种类加载环境中都会由启动类加载器来加载,换言之,它无论在什么环境中都是同一个Object类。但是有时候我们可能需要“打破”双亲委派模型。双亲委派模型让我们加载基础类的时候都是同一个基础类,但我们有时候可能需要在基础类中回调用户代码怎么办呢?“基础类中回调用户代码”可能不大好理解,我们列举一个例子来说明:Java提供了很多服务提供者接口(SPI,Service Provider Interface),允许独立厂商(第三方)为此提供实现。常见的SPI有:JNDI、JDBC、JAXP等。这些接口由Java的核心库来提供,所以问题就在于,SPI的接口是Java核心库的一部分,它们是由启动类加载器来加载的。SPI实现的Java类一般是由应用程序类加载器(Application ClassLoader)来加载的。启动类无法找到SPI的实现类,因为它只加载核心库(SPI的实现类由第三方提供)。它也不能代理给应用程序类加载器,因为它又是应用程序类加载器的父类,双亲委派模型又会将它交给启动类来加载。所以在这个时候我们就要“打破”这个“双亲委派模型”。

这个时候,线程上下文类加载器(Thread Context ClassLoader)很好地解决了这个问题。Thread类中有getContextClassLoader()和setContextClassLoader(ClassLoader cl)方法用来获取和设置上下文类加载器,如果没有setContextClassLoader(ClassLoader cl)方法通过设置类加载器,那么线程将继承父线程的上下文类加载器,如果在应用程序的全局范围内都没有设置的话,那么这个上下文类加载器默认就是应用程序类加载器(Application ClassLoader),换句话说Java默认的线程上下文类加载器就是应用程序类加载器(AppClassLoader)。通过线程上下文来加载第三方库jndi实现,而不依赖于双亲委派。大部分Java Application服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务(所以理解线程上下文类加载器,更能让我们理解Tomcat等服务器的实现原理、工作方式)。

虚拟机的这几个部分确实有难度,本人经验知识有限也在努力学习中,尚不能给出专业且详尽的分析,这里有一篇介绍上下文类加载的文章可以研读此文。http://blog.csdn.net/zhoudaxia/article/details/35897057

时间: 2024-10-08 18:07:48

虚拟机类加载机制(3)——线程上下文类加载器的相关文章

线程上下文类加载器与服务器类加载原理

双亲委派机制以及类加载器的问题 一般情况下.保证同一个类中所关联的其他类都是由当前类的类加载器所加载的. 比如,class A本身在Ext下找到.那么他里面new出来的一些类也就只能用Ext去查找了(不会低一个级别).所以有些明明App可以找到的,却找不到了. JDBC API他有实现的driver部分(mysql,sql server).我们的JDBC APl都是由Boot或者Ext来载入的.但是JDBC driver却是由Ext或者App来载入,那么就有可能找不到driver了.在Java领

线程上下文类加载器

Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现.常见的 SPI 有 JDBC.JCE.JNDI.JAXP 和 JBI 等. 这些 SPI 的接口由 Java 核心库来提供,而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进类路径(CLASSPATH)里.SPI接口中的代码经常需要加载具体的实现类.那么问题来了,SPI的接口是Java核心库的一部分,是由启动类加载器来加载的:SPI的实现类

线程上下文类加载器ContextClassLoader内存泄漏隐患

前提 今天(2020-01-18)在编写Netty相关代码的时候,从Netty源码中的ThreadDeathWatcher和GlobalEventExecutor追溯到两个和线程上下文类加载器ContextClassLoader内存泄漏相关的Issue: ThreadDeathWatcher causes custom classLoader script memory leaks Ensure ThreadDeathWatcher and GlobalEventExecutor will no

线程上下文类加载器分析与实现

在上一次[https://www.cnblogs.com/webor2006/p/9246850.html]分析源码中发现有两处设置线程上下文类加载器的代码,如下: 因为它是非常重要的东东,所以这次专门对它进行主题展开,主要的作用为了改变委托双亲模式在某些场景不太适用或者是无法满足需求的,下面先写一个简单的测试代码: 那输出是啥呢? 也就是说当前线程的上下文类加载器是应用类加载器,而第二输出null不足为奇,因为Thread是JDK中的系统类当然是由启动类加载器加载喽. 对于上面的例子先有一个初

类加载机制的学习1______类加载器

在学习类加载机制之前,我们先了解一下类加载器,因为类加载器是类加载机制的前提.类加载器的主要任务就是:根据一个类的全限定名,将该类的字节码文件加载进JVM中,然后转换为一个对应类的Java.lang.Class对象实例.程序员也可以自定义类加载器,一般的将派生于抽象类ClassLoader的类加载器都划分为自定义类加载器. 在程序中我们最常见的类加载器时钟只有三个: Bootstrap ClassLoader ExtClassLoader: AppClassLoader:(加载classpath

虚拟机类加载机制(2)——类加载器

<深入理解Java虚拟机>一书中将类的加载过程放到了类加载器前面一节,但在这里我想先讲“类加载器”.在上一篇类加载时机中我们用大量篇幅来讲解了类加载过程中的5个步骤的最后一步——初始化.在这一节中,我们实际是在讲解类加载过程5个步骤的第一步——加载. 我们再次回顾类加载过程的5个步骤: 类加载过程的“加载”步骤是通过类加载器(ClassLoader)来实现的.那么加载阶段做什么工作呢?“通过一个类的全限定名来获取描述此类的二进制字节流.”首先我们需要了解来自同一个Class文件的两个类是否一定

Python虚拟机函数机制之闭包和装饰器(七)

函数中局部变量的访问 在完成了对函数参数的剖析后,我们再来看看,在Python中,函数的局部变量时如何实现的.前面提到过,函数参数也是一种局部变量.所以,其实局部变量的实现机制与函数参数的实现机制是完全一样的.这个"一样"是什么意思呢? 之前我们剖析过Python虚拟机的一些指令,如果要访问一个变量,应该使用LOAD_NAME指令,应该依照local.global.builtin这三个名字空间里去检索变量名所对应的变量值.然后在调用函数时,Python虚拟机通过PyFrame_New创

学习java虚拟机 - 类加载机制

学习java虚拟机 - 类加载机制  一.是什么 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 在Java语言里面,类型的加载.链接.初始化过程都是在程序运行期间完成的,Java里天生可以动态扩展的语言特性就是依赖运行期间动态加载和动态连接这个特点实现的.例如,如果编写一个面向接口的应用程序,可以等到运行时在制定实际的实现类:用户可以通过Java预定义的和自定义类加载器,让一个本地的应用程序

Java虚拟机的类加载机制

Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需要完成以下3件事: a.通过一个类的全限定名来获取定义此类的二进制字节流. b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构. c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口. Java虚拟机的类加载是通过类加载器实现的, Java中