java中类加载器ClassLoader,双亲加载机制,启动类加载器,应用类加载器,线程上下文类加载器
我们知道,在java中一般我们都是以jar包或者war包的形式发布应用,而这些里面的class文件是需要在JVM虚拟机中运行,那么这些class类文件怎么加载到jvm中的呢 ?
一个类从加载到虚拟机内存中一直到卸载出内存为止,要经历如下几个阶段
- 加载(Loading),通过类的全名获取类的字节流,然后将其转换为方法区的运行时数据结构,同时在内存中生成一个代表该类的java.lang.Class对象,作为方法区该类的的各种数据的访问入口
- 验证(Vertification),确保加载的Class信息符合java虚拟机规范的全部要求
- 准备(Preparation),为类中定义的静态变量分配内存空间并设置初始值
- 解析(Resolution),将常量池中的符号引用替换为直接引用,包含类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符这7类符号引用替换为类信息在内存中的直接引用
- 初始化(Initialization),初始化类变量和其他资源
- 使用(Using)
- 卸载(Unloading)
而验证、准备、解析三个阶段称为连接(Linking)
这个就是大家看类加载基本上都能看到的内容。
类加载器
在java中类加载器用来加载类二进制文件到JVM中,一个类在JVM中类型是否相同除了比较类的全名是否一致,还需要判断是否由同一个类加载器加载到JVM中,即判断类的类型是否一样前提条件是必须是同一个类加载器
,类加载器有如下三个特性:
- 委托机制,子类加载一个类首先委托父类去加载,如果父类加载不了才会自己去加载
- 可见性,子类可以看见并访问父类加载的所有类,但是父类加载器加载的类看不到子类加载器加载的类
- 单一性,类加载器仅加载一个类一次,委托机制确保子类不会在加载父类已经加载的类
在java中,如果classA中有classB的引用,那么当加载classA的时候发现需要加载classB,就会用加载classA的类加载器去加载classB
双亲委派模型
jvm中类加载器加载类的时候采用的是双亲委派模型:当一个类加载器需要加载一个类的时候,并不会立马自己去记载,而是首先委派给父类加载器去加载,父类加载器加载不了在给父类的父类去加载,一层一层往上委托,直到顶层加载器(Bootstrap Classloader),如果父类加载器反馈无法加载那么累加器才会自己去加载。
需要注意的是,双亲委派模型中,子类是可以看到并且访问父类加载的类,反之不行,即父类无法看到访问子类加载的类
JVM默认提供了三种类加载器:
- 启动类加载器(BootStrap ClassLoader),加载在JAVA_HOME/lib目录下或-Xbootclasspath参数指定的路径且能够被jvm识别的(按照文件名识别,如rt.jar、tools.jar,名字不符合即使放在lib目录下也不会被加载)
- 扩展类加载器(Extension ClassLoader),加载在
JAVA_HOME/lib/ext
目录下,或者被java.ext.dirs
系统变量所指定的的路径中的类,可以直接将常用的类放在JAVA_HOME/lib/ext目录下,让扩展类加载器加载 - 应用程序类加载器(Applicaiton ClassLoader),加载用户类路径下的所有类库
classpath
,也被称为系统类加载器,如果没有自定义过类加载器,这就是程序中默认的类加载器
双亲委派机制保证了一个类只会被加载一次,同时java核心类库不会被篡改,即使篡改了也不会被加载
线程上下文加载器
但是java中也有破坏双亲模型的机制,例如SPI(Service Provider Interface),但是SPI的接口是java核心类库(如ServiceLoader),是由启动类加载器加载的,但是实现都是第三方实现,一般都是由应用程序类加载器加载的(例如mysql驱动: com.mysql.jdbc.Driver)。我们看看JDBC的加载机制:
Connection connection = DriverManager.getConnection(url,username,password);String sql = "select count(1) from test ";Statement statement = connection.prepareStatement(sql);ResultSet rs = statement.executeQuery(sql);while(rs.next()){int result = rs.getInt(1);System.out.println(result);}
上面这段不用Classs.forName
也是可以的,就是用到了SPI机制:
public class DriverManager {static {loadInitialDrivers();println("JDBC DriverManager initialized");}...}ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {// load classdriversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}
最后实际上是加载META-INF/services/java.sql.Driver
文件里面的类:
最后需要加载如下两个类:
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
也就是说,需要在在加载核心类库中的类ServiceLoader
加载的时候加载上述两个类。按照上面描述的:
在java中,如果classA中有classB的引用,那么当加载classA的时候发现需要加载classB,就会用加载classA的类加载器去加载classB
这时候,加载ServiceLoader
用的是启动类加载器,按照双亲委托机制是无法加载第三方类的。这时候java提供了线程上下文类加载器,相当于是破坏了双亲加载机制。
那么线程上下文类加载器怎么使用的呢 ?
public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);}
public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){return new ServiceLoader<>(service, loader);}
private ServiceLoader(Class<S> svc, ClassLoader cl) {service = Objects.requireNonNull(svc, "Service interface cannot be null");loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;reload();}
public void reload() {providers.clear();lookupIterator = new LazyIterator(service, loader);}
private S nextService() {if (!hasNextService())throw new NoSuchElementException();String cn = nextName;nextName = null;Class<?> c = null;try {c = Class.forName(cn, false, loader);} catch (ClassNotFoundException x) {fail(service,"Provider " + cn + " not found");}if (!service.isAssignableFrom(c)) {fail(service,"Provider " + cn + " not a subtype");}try {S p = service.cast(c.newInstance());providers.put(cn, p);return p;} catch (Throwable x) {fail(service,"Provider " + cn + " could not be instantiated",x);}throw new Error(); // This cannot happen}
可以看到,这里通过ClassLoader cl = Thread.currentThread().getContextClassLoader();
获取到了线程上线文类加载器,然后用这个去加载第三方类库,这样启动类加载器加载的类就能够看到线程上下文类加载器加载的类了,打破了双亲委派机制。
我们在看看tomcat的类加载器的应用:
tomcat中自己定义了几个类加载器,主要分为如下几个:
- common类加载器,这个类加载器加载的类能被所有的程序共享,包含tomcat和tomcat下的web程序
- catalina类加载器,这个类加载器加载的类只能被tomcat使用,对web程序不可见
- shared类加载器,可以被所有的web程序公用,但是tomcat无法使用
可以看到,通过自定义的类加载器和双亲委派机制,tomcat实现了不同层次类之间的隔离。
在spring中,用的大部分也都是线程上下文类加载器。
java中类加载器ClassLoader,双亲加载机制,启动类加载器,应用类加载器,线程上下文类加载器相关推荐
- Java中的类加载器(ClassLoader)及类的加载机制
类加载器就是用来将class文件加载到内存中的一个java类! 系统默认有三个类加载器! ①BootStrap:这不是java类,是java虚拟机在启动的时候加载的第一个类,这个加载器用来加载核心类, ...
- 【java】ClassLoader.getResources加载资源的顺序
目录 前言 代码说明 目录结构 FromMultiJar/pom.xml 代码 编译后目录结构 启动命令 ClassLoader.getResources加载资源的顺序 先在本应用(FromMulti ...
- WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探
原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探 最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感觉WPF的开发思 ...
- JVM类加载理解(线程上下文类加载器、Tomcat类加载器)
类加载机制概念 Java虚拟机把描述类的class文件加载到内存,对其进行校验.转换解析.初始化等操作,最终得到可以被虚拟机直接使用的java类型,这就是虚拟机的加载机制. 主要有五个步骤: 加载 将 ...
- 重置线程中断状态_记住要重置线程上下文类加载器
重置线程中断状态 我很难思考与Java 加载有关的东西,而不是与类加载器有关的东西. 在使用应用程序服务器或OSGi的情况下尤其如此,在这些应用程序服务器或OSGi中,经常使用多个类加载器,并且透明地 ...
- 记住要重置线程上下文类加载器
我很难思考与Java 加载有关的东西,而不是与类加载器有关的东西. 在使用应用程序服务器或OSGi的情况下尤其如此,在这些应用程序服务器或OSGi中,经常使用多个类加载器,并且透明地使用类加载器的能力 ...
- 28 Java类的加载机制、什么是类的加载、类的生命周期、加载:查找并加载类的二进制数据、连接、初始化、类加载器、双亲委派模型、自定义类加载器
28Java类的加载机制 28.1.什么是类的加载 28.2.类的生命周期 28.2.1.加载:查找并加载类的二进制数据 28.2.2.连接 28.2.3.初始化 28.3.类加载器 28.4.类的加 ...
- 利用classloader同一个项目中加载另一个同名的类_线程上下文类加载器ContextClassLoader内存泄漏隐患...
前提 今天(2020-01-18)在编写Netty相关代码的时候,从Netty源码中的ThreadDeathWatcher和GlobalEventExecutor追溯到两个和线程上下文类加载器Cont ...
- java 类加载器的理解及加载机制?
通过 java 命令运行 java 程序的步骤就是指定包含 main 方法的完整类名以及一个 classpath 类路径,类路径可以有多个,对于直接的 class 文件路径就是 class 文件的根目 ...
- JAVA类加载机制之Classloader以及打破加载机制的方式
在jDK1.8中,Classloader加载class的字节码到JVM,它是遵循双亲委派模型的加载机制,主要是由BootstrapClassLoader,ExtClassLoader.AppClass ...
最新文章
- python django 优势_那么多人选择Python,它的优势,缺点有哪些?
- 原来这就是Java代码生成器的原理啊,太简单了
- 计算机视觉开源库OpenCV之threshold()函数详解
- Git 基本操作教程
- JZOJ 5628. 【NOI2018模拟4.4】Travel
- python课程设计小程序_python实验课做的一些小程序
- Jenkins自动化CI CD流水线之8--流水线自动化发布Java项目
- 宁夏2021高考成绩查询,宁夏教育考试院:2021年宁夏高考成绩查询入口、查分系统...
- 一个简单的把对象序列化/反序列化为json格式的程序
- SpringMVC 工作原理详解
- 学计算机打字一段话,如何快速学会电脑打字
- 对称加密php,PHP实现对称加密与解密
- 90%的 CTO 都做不好绩效管理?看看这个十年 IT 老兵都有什么绝活?
- 计算机对幼儿教育发展的优点,音乐教育对幼儿发展的影响
- Java编写猜单词游戏
- 表情包组件(vue)
- 计算机网络——运输层
- sizeof c语言是什么表达式,C 语言 sizeof() 运算符
- linux申请信号量,linux 信号量
- @WebFilter创建未生效的解决办法