JVM类加载(4)—加载器
定义:
虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称之为“类加载器“。
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器都有一个独立的类名称空间。简单来讲,比较两个类是否“相等”,必须是这两个类由同一个类加载器加载的才有意义,否则,即使这两个类来源于同一个class、被同一个虚拟机加载,只要这两个类是由不同的加载器加载,那么这两个类一定不相等。
从Java虚拟机角度上来讲,只存在两种不同的类加载器:一种是启动类加载器又叫根类加载器(Bootstrap ClassLoader ),这个类是由C++实现的,是虚拟机的一部分;另一种就是其他所有类加载器,这些加载器都是有Java实现,独立于虚拟机外部,都继承自抽象类java.lang.ClassLoader。
类加载机制
类加载器的加载机制采用双亲委派模型(Parents Delegation Model)或者说是父亲委托机制。双亲委派模型要求除了顶层是启动类加载器之外,其余的类加载器都应有自己的父类加载器,这里类加载器之间的父子关系一般不会使用继承方式来实现,而都是使用组合关系来复用父类的代码,Java虚拟机为我们提供了三种类加载器,最顶层的启动类加载器、然后是扩展类加载器(Extension ClassLoader)、应用程序类加载器(Application ClassLoader)又叫系统类加载器,用户如果有需要还可以自定义类加载器,继承java.lang.ClassLoader抽象类实现自定义加载器,所有的自定义加载器都需要指定一个父类加载器,未指定父类加载器的情况下,默认父类加载器为应用程序类加载器。从下面的ClassLoader中的源码片段就可以看出来:
//这个构造方法是指定父类加载器的,通过构造方法传进来的是父类加载器
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
} //不带参数的构造方法,默认会将父类加载器指定为应用程序类加载器, 、
//getSystemClassLoader这个方法会获取应用程序类加载器
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类加载器进行加载,每一个层次的类加载器都是如此,因此最终所有的请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于最顶端的启动类加载器进行加载,因此Object类在各种类加载环境中都是一个类。相反如果没有采用双亲委派模型,由各个类加载器进行各自加载的话,如果用户自己编写了一个java.lang.Object类,并放在classpath中,那系统将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证。双亲委派模型的实现很简单,实现代码都集中在ClassLoader抽象类的loadClass方法中,如下代码清单,逻辑清晰易懂,首先检查类是否被加载,若没有则调用父类加载器进行加载,若父加载器为空则默认使用启动类加载器作为父加载器进行加载。如果父类加载失败后抛出ClassNotFoundException异常之后,再调用自己的findClass方法进行加载,代码如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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;
}
下面是Java虚拟机提供的几种类加载器的详细介绍
- 启动类加载器(Bootstrap)、又叫根类加载器:该加载器没有父类加载器,它由C++实现,它负责加载虚拟机的核心类库,如java.lang.*等,它从<JAVA_HOME>\lib中、或者是被-Xbootclasspath参数所指定的路径中、并且是虚拟机所识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使被放在lib中也不会被加载)的类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用,它没有继承java.lang.ClassLoader类,用户在编写自定义的类加载器时,如果需要把加载请求委派给启动类加载器,那直接用null代替即可,如下代码所示,为ClassLoader.getClassLoader代码片段:
// Returns the class's class loader, or null if none.
static ClassLoader getClassLoader(Class<?> caller) {
// This can be null if the VM is requesting it
if (caller == null) {
return null;
}
// Circumvent security check since this is package-private
return caller.getClassLoader0();
}
- 扩展类加载器(Extension ClassLoader):这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
- 应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher$App-ClassLoader,由于这个类加载器是ClassLoader中的getSystemClassLoader方法的返回值,所以一般也称它为系统类加载器。它负责加载用户路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
命名空间:
每一个类加载器都有自己的命名空间,命名空间由该类加载器及所有父加载器所加载的类组成。在同一个命名空间中,不会出现类的完整名字相同的两个类,在不同的命名空间中则有可能出现。
运行时包:
由同一类加载器加载的属于相同包的类组成运行时包。决定两个类是不是属于一个运行时包,不仅看它们的包名是否相同,还要看定义类加载器是否相同。只有属于同一运行时包的类才能互相访问包可见(即默认包访问权限以及由protect修饰的类及类成员)的属性。这样的限制能避免用户自定义的类冒充核心类库去访问核心类的包可见成员。假设用户自定义了一个java.lang.Test类,并由自定义加载器加载,Test类并不能访问核心类库java.lang.*中的包可见成员。
JVM类加载(4)—加载器的更多相关文章
- JVM笔记11-类加载器和OSGI
一.JVM 类加载器: 一个类在使用前,如何通过类调用静态字段,静态方法,或者new一个实例对象,第一步就是需要类加载,然后是连接和初始化,最后才能使用. 类从被加载到虚拟机内存中开始,到卸载出内存为 ...
- 深入JVM之类的加载器
类加载器有两种: —java虚拟机的自带加载器 根类加载器(Bootstrap) 扩展类加载器(Extension) 系统类加载器(AppClassLoder) —自定义的类加载器 java.lang ...
- Java类加载机制及自定义加载器
转载:https://www.cnblogs.com/gdpuzxs/p/7044963.html Java类加载机制及自定义加载器 一:ClassLoader类加载器,主要的作用是将class文件加 ...
- jvm(1)类的加载(三)(线程上下文加载器)
简介: 类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的. Java Applet 需要从远程下载 Java 类文件到浏览器中并执行. 现在类加载器在 ...
- JVM学习二:JVM之类加载器之加载分析
前面一遍,我们对类的加载有了一个整体的认识,而这一节我们细节分析一下类加载器的第一步,即:加载. 一.概念 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区 ...
- Java 理解类加载过程 -- 自定义加载器
类加载器可以看下我的收藏: https://www.cnblogs.com/dongguacai/p/5879931.html 现在准备一个字节码文件: 自定义加载器: package com.xzl ...
- <JVM中篇:字节码与类的加载篇>04-再谈类的加载器
笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...
- java 27 - 1 反射之 类的加载器
说到反射,首先说类的加载器. 类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载: 就是指将class文件读入内存,并为之 ...
- Servlet加载器的实验
今天,看了张孝祥老师的类加载器的一个高级实验分析的教程,有点受益匪浅. 新建servlet工程,在Servlet类中 package com.sinosoft.servelt; import java ...
随机推荐
- Android系统字体规范
我们在做Android移动APP设计的时候,字号的选择也是很让人头疼,转载一份有关Android系统字体规范,如果在做Android项目的用户应该看看,如果有任何建议欢迎在留言处与我们交流探讨. 主要 ...
- SAP HR 复制PA30的人员
[转自http://www.512test.com/home/space.php?uid=19&do=blog&id=2381] 很多顾问测试HR的程序时都为录入人员头痛,下面的程序提 ...
- IoC与DI
IoC与DI 首先想说说IoC(Inversion of Control,控制倒转).这是spring的核心,贯穿始终.所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期 ...
- STM32L0 复位和时钟控制 Reset and clock control (RCC)
时钟源: HSE:外部时钟 HSI16:可以直接用于系统时钟或者作为PLL输入.一般是1%精度 HSI48:The HSI48 clock signal is generated from an in ...
- 【leetcode刷题笔记】Sort List
Sort a linked list in O(n log n) time using constant space complexity. 题解:实现一个链表的归并排序即可.主要分为三部分: 1.找 ...
- P3320 [SDOI2015]寻宝游戏
题目 P3320 [SDOI2015]寻宝游戏 做法 很巧妙的一种思路,懂了之后觉得大水题 首先要知道:在一棵树上标记一些点,然后从任意一点出发,遍历所有的的最小路径为\(dfs\)序从小到大遍历 那 ...
- Linux 上通过rpm安装mysql
安装mysql之前要remove掉系统自带的mysql: rpm -qa | grep "MySQL*" 和rpm -qa | grep mysql 要确保卸载干净 rpm ...
- web应用组成结构,web.xml的作用
- Java -- AWT 菜单建立, Menu, 右键菜单
1. Menu类结构 2. 菜单示例: MenuBar容器中可以装Menu,Menu容器中可以装MenuItem. public class SimpleMenu { Frame f = new F ...
- linux lvm
一.linux的lv(logical volume) lv各层次示例图如下: 核心思想:最底层的pv就是一个一个的磁盘,在保证总体容量的情况下,可以移除部分磁盘,在pv上面设置一个vg,相当于vg把所 ...