定义:

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

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在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. 1.搭建Django开发环境

    1.安装python(版本3.5.1) 官网下载:https://www.python.org/downloads/release/python-351/2.更新pip 命令:python -m pi ...

  2. Swift学习笔记十一:方法

         方法是与某些特定类型相关联的功能/函数.在Swift中,结构体和枚举能够定义方法:其实这是Swift与C/Objective-C的主要差别之中的一个. 在Objective-C中,类是唯一能 ...

  3. shell if判断-n

    test测试命令 test命令用于检查某个条件是否成立,它可以进行数值.字符串和文件三个方面的测试,其测试符和相应的功能分别如下: (1)数值测试: -eq:等于则为真        -ne:不等于则 ...

  4. python学习-3.一些常用模块用法

    一.time.datetime 时间戳转化为元组 1 >>> time.localtime() 2 time.struct_time(tm_year=2016, tm_mon=8, ...

  5. awk 运算符(算术运算符,赋值运算符,关系运算符,逻辑运算符,正则运算符)说明

    awk作为文本处理优秀工具之一,它有独自丰富的运算符.下面我们一起归纳总结一下,所有运算符. 可以分为:算术运算符,赋值运算符,关系运算符,逻辑预算法,正则运算符. 一.运算符介绍 运算符 描述 赋值 ...

  6. python基础8 -----迭代器和生成器

    迭代器和生成器 一.迭代器 1.迭代器协议指的是对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2. ...

  7. python基础4 ----字符编码

    python基础---字符编码 一.了解字符编码 1. 文本编辑器存取文件的原理(nodepad++,pycharm,word) 打开编辑器就打开了启动了一个进程,是在内存中的,所以在编辑器编写的内容 ...

  8. 基于WebServices简易网络聊天工具的设计与实现

    基于WebServices简易网络聊天工具的设计与实现 Copyright 朱向洋 Sunsea ALL Right Reserved 一.项目内容 本次课程实现一个类似QQ的网络聊天软件的功能:服务 ...

  9. STL+位运算的文件

    1.queue 队列 queue的头文件是<queue>. 定义queue对象的示例代码如: queue<int>q;  队列内存放的是int类型的数 queue<dou ...

  10. 大话设计模式--工厂方法模式 Factory Method -- C++实现

    1. 工厂方法模式 定义一个用于创建对象的接口, 让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类. 和简单工厂模式相比: A: 简单工厂模式最大的优点在于工厂类中包含有必要的逻辑判断, ...