定义:

虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称之为“类加载器“。

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在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)—加载器的更多相关文章

  1. JVM笔记11-类加载器和OSGI

    一.JVM 类加载器: 一个类在使用前,如何通过类调用静态字段,静态方法,或者new一个实例对象,第一步就是需要类加载,然后是连接和初始化,最后才能使用. 类从被加载到虚拟机内存中开始,到卸载出内存为 ...

  2. 深入JVM之类的加载器

    类加载器有两种: —java虚拟机的自带加载器 根类加载器(Bootstrap) 扩展类加载器(Extension) 系统类加载器(AppClassLoder) —自定义的类加载器 java.lang ...

  3. Java类加载机制及自定义加载器

    转载:https://www.cnblogs.com/gdpuzxs/p/7044963.html Java类加载机制及自定义加载器 一:ClassLoader类加载器,主要的作用是将class文件加 ...

  4. jvm(1)类的加载(三)(线程上下文加载器)

    简介: 类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的. Java Applet 需要从远程下载 Java 类文件到浏览器中并执行. 现在类加载器在 ...

  5. JVM学习二:JVM之类加载器之加载分析

    前面一遍,我们对类的加载有了一个整体的认识,而这一节我们细节分析一下类加载器的第一步,即:加载. 一.概念 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区 ...

  6. Java 理解类加载过程 -- 自定义加载器

    类加载器可以看下我的收藏: https://www.cnblogs.com/dongguacai/p/5879931.html 现在准备一个字节码文件: 自定义加载器: package com.xzl ...

  7. <JVM中篇:字节码与类的加载篇>04-再谈类的加载器

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  8. java 27 - 1 反射之 类的加载器

    说到反射,首先说类的加载器. 类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载: 就是指将class文件读入内存,并为之 ...

  9. Servlet加载器的实验

    今天,看了张孝祥老师的类加载器的一个高级实验分析的教程,有点受益匪浅. 新建servlet工程,在Servlet类中 package com.sinosoft.servelt; import java ...

随机推荐

  1. window 上创建 .gitignore文件

    由于 git默认不上传空文件夹,如果需要上传空文件夹,那么需要这样上传空文件,官方给出这样的做法~~ (需要创建.gitignore文件) 在linux 上比较好操作了,这里说下在window 上 创 ...

  2. python基础16 ----面向对象程序设计二

    一.继承与派生 1.继承的定义:继承是一种创建新类的方式,即在类中提取共同的部分创建出一个类,这样的类称为父类,也可称为基类和超类,新建的类称为派生类或子类. 2.单继承:就相当于子类继承了一个父类. ...

  3. Havel-Hakimi定理 POJ1659

    对于图的所有顶点,计算出每个顶点的度,度序列.给定一个序列判断序列是否可图. #include<cstdio> #include<algorithm> #include< ...

  4. 算法(Algorithms)第4版 练习 2.2.9

    package com.qiusongde; import edu.princeton.cs.algs4.In; import edu.princeton.cs.algs4.StdOut; publi ...

  5. Bootstrap(一)标题

    Bootstrap标题样式进行了以下显著的优化重置: 1.重新设置了margin-top和margin-bottom的值,  h1~h3重置后的值都是20px:h4~h6重置后的值都是10px.2.所 ...

  6. 封装 oschina.net 表情选择

    1. [代码]jquery.facial.js //从OSCHINA.NET 提取出来的 表情选择 插件 by zhouxiang //如果有不满足的地方 可以自己改改 没事随便写写的 style 和 ...

  7. string(未完待续)

    1.string字符串的长度     可以用   a.length()  来测,或者是a.size() 来测 不可以用strlen(a)来求其长度, sizeof(a)是固定值16, 求的是strin ...

  8. 2017各银行贷款利息表及P2P平台贷款利率比较

    银行贷款利息是多少?2017各银行贷款利息表及P2P平台贷款利率比较 发表时间: 2017-02-17 作者: 一.2017央行贷款基准率 各个银行的贷款利率是以中国银行的人民币贷款基准率为标准进行上 ...

  9. 201621123014《JAVA程序设计》第2周学习总结

    1. 本周学习总结 引用数据类型:JAVA定义字符串实际上是创建字符串的引用,将引用指向需要的字符串. 字符串常量池:直接对引用赋值时,会先在字符串中搜索是否有这个对象,已有则不创建直接指向它. St ...

  10. 用截取的部分图像创建新图像--关于cvGetSubRect,cvGetImage的用法

    CvMat* cvGetSubRect(const CvArr* arr, CvMat* submat, CvRect rect)可以把截取图像中需要的区域存入矩阵.把IplImage *传给arr, ...