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 ...
随机推荐
- centos7 使用 maven
http://www.cnblogs.com/jackluo/archive/2013/02/06/2901816.html
- Android 主线程和子线程通信问题
Android 如今不支持View在子线程中创建及调用其方法.假设要实现子线程内容更新之后.将结果及时反馈到主线程中,该怎样出来呢? 能够在主线程中创建Handler来实现. 这样子线 ...
- R语言图形base系统(二)
x<-c(1:10) y<-x z<-10/x opar<-par(no.readonly = T) par(mar=c(5,4,4,8)+0.1) plot(x,y,type ...
- Ubuntu 14.04 或者16.04开启root账户登录和图形界面登录root时候的报错解决方法
1.打开终端 2.输入sudo vi /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf 3.添加一行:greeter-show-manual-login ...
- 好久没更了,确实太忙了--dedecms篇
最近写了一个地方新闻网站,可以看看:www.qiluhuabao.com.模仿www.bashan.com写的 用的是dedecms,只听过,没用过.终于在上周来了一个必须用到cms的项目,现学现卖, ...
- Spark集群搭建(local、standalone、yarn)
Spark集群搭建 local本地模式 下载安装包解压即可使用,测试(2.2版本)./bin/spark-submit --class org.apache.spark.examples.SparkP ...
- Bootstrap(二)段落+强调内容
在Bootstrap中为文本设置了一个全局的文本样式(这里所说的文本是指正文文本): 1.全局文本字号为14px(font-size). 2.行高为1.42857143(line-height),大约 ...
- jQuery-中的事件
[jQuery中的事件] javascript和html之间的交互是通过用户和浏览器操作页面时引发的事件来处理的,虽然传统的javascript能完成这些交互,但事jQuery增加并扩充了基本事件处理 ...
- 关于ios::sync_with_stdio(false);和 cin.tie(0)加速c++输入输出流
原文地址:http://www.hankcs.com/program/cpp/cin-tie-with-sync_with_stdio-acceleration-input-and-output.ht ...
- 理解VMware虚拟网络
简述:VMware虚拟网络概述.实现虚拟网络上网 Part0 子网掩码.DHCP.NAT,这些点请自行百度,百度百科讲的很清晰. Part1 转载:本文出自 "王春海的博客" 博客 ...