类的生命周期中的第一步,就是要被 JVM 加载进内存,类加载器就是来干这件事。

一、类加载器种类

系统提供了 3 种类加载器:

1.启动类加载器(Bootstrap ClassLoader)
由 C 和 C++ 编写,是在 JVM 启动后初始化的。可在这里查看到源码(OpenJDK):http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/933f6b06c1cb/src/share/native/java/lang/ClassLoader.c
负责将存放在 <JAVA_HOME>\jre\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且能被虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。 2.扩展类加载器(Extension ClassLoader)
由 sun.misc.Launcher$ExtClassLoader 实现,负责加载 <JAVA_HOME>\jre\lib\ext 目录中的所有类库,以及系统变量 java.ext.dirs 指定路径中的所有类库,开发者可以直接使用扩展类加载器。 3.应用程序类加载器(Application ClassLoader)
由 sun.misc.Launcher$AppClassLoader 实现,可以通过 ClassLoader 类中的 getSystemClassLoader() 方法的获得,所以一般也称它为“系统类加载器”。
它负责加载用户类路径(classpath:CLASSPATH 环境变量指定的, 由 -classpath 或 -cp 选项定义的,或者是 jar 中的 Manifest 的 classpath 属性定义的)上所指定的类库,以及系统变量 java.class.path 指定路径中的所有类库。
开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器(父子关系一般不会以继承的关系实现,而是以组合关系来复用父加载器的代码),结构如图:

getParent() 可获得父加载器

public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
// 默认由 AppClassLoader 加载类
System.out.println(classLoader);
// ExtClassLoader
System.out.println(classLoader.getParent());
// Bootstrap ClassLoader,由 JVM 启动
System.out.println(classLoader.getParent().getParent());
}
}

除系统提供的加载器外,还可以自己定义类加载器。(继承 java.lang.ClassLoader 类实现)

二、类加载器工作方式(加载机制)

2.1.委托机制(委派模型 或 父委派模型)

委派模型是描述类加载器之间的层次关系。

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。

因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(找不到所需的类)时,子加载器才会尝试自己去加载。

在 java.lang.ClassLoader 中的 loadClass() 方法中实现该了过程。

public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 首先,检查是否已加载该类
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) {
// 如果从非 null 的父类加载器中找不到该类,则抛出 ClassNotFoundException
} if (c == null) {
long t1 = System.nanoTime();
// 如果仍未找到,则调用 findClass 查找该类
c = findClass(name); // 这是定义的类加载器; 记录统计数据
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
// 解析类,属于类加载的 link 阶段
resolveClass(c);
}
return c;
}
} /**
* ClassLoader 的子类建议重写 findClass 方法,而不是 loadClass
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}

自己写的 java.lang.String 类,是否可以替换 JDK 自带的类?

答案是不行的。但这非委托机制解决的,因为委托机制是可以被打破的,完全可以写一个 classLoader 来加载自己写的 java.lang.String 类。

但是你会发现也加载不成功,因为 JVM 的实现中已经保证了 java.* 开头的类必须由 bootstrp 来加载。
 

2.2.可见性机制

子类加载器可以看到父类加载器加载的类,而反之则不行。当 Abc.class 已经被 Application 类加载器加载过了,然后想要使用 Extension 类加载器加载这个类,将会抛出 java.lang.ClassNotFoundException 异常。

2.3.单一性机制

根据委托机制,父加载器加载过的类不能被子加载器加载第二次。虽然重写 loadClass() 的类加载器可以做到不遵守委托机制和单一性机制,但这样做并不可取。

判断类是否“相等”

任意一个类,都由加载它的类加载器和这个类本身一同确立其在 Java 虚拟机中的唯一性,每一个类加载器,都有一个独立的类名称空间。

因此,比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class 文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相等。

这里的“相等”,包括代表类的 Class 对象的 equals() 方法、isInstance() 方法的返回结果,也包括使用 instanceof 关键字做对象所属关系判定等情况。

关于破坏委派模型

java 引入了线程上下文类加载器(Thread Context ClassLoader),这个类加载器可以通过 Thread 类的 setContextClassLoader 进行设置,默认继承父线程类加载器,也可由父类加载器请求子类加载器完成类加载动作。

https://www.jianshu.com/p/09f73af48a98


https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html

https://docs.oracle.com/javase/tutorial/ext/basics/load.html

https://blog.csdn.net/lengxiao1993/article/details/86689331

https://github.com/doocs/jvm/blob/master/docs/10-class-loader.md

Java-JVM 类加载机制的更多相关文章

  1. Java JVM类加载机制

    虚拟机的类加载机制是:JVM把描述类的数据从.class文件加载到内存,并对数据进行校验.解析.初始化,最终形成可以被JVM直接使用的Java类型. 加载.连接(验证.准备.解析).初始化.使用.卸载 ...

  2. Java虚拟机(四):JVM类加载机制

    1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...

  3. Java虚拟机(五):JVM 类加载机制

    一.JVM 类加载机制 JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 1. 加载: 加载是类加载过程中的第一个阶段,这个阶段会在内存中生成一个代表 ...

  4. Java基础篇(JVM)——类加载机制

    这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...

  5. Java虚拟机类加载机制——案例分析

    转载: Java虚拟机类加载机制--案例分析   在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬 ...

  6. JVM基础系列第7讲:JVM 类加载机制

    当 Java 虚拟机将 Java 源码编译为字节码之后,虚拟机便可以将字节码读取进内存,从而进行解析.运行等整个过程,这个过程我们叫:Java 虚拟机的类加载机制.JVM 虚拟机执行 class 字节 ...

  7. Java 的类加载机制

    Java 的类加载机制 来源 https://www.cnblogs.com/xiaoxi/p/6959615.html 一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内 ...

  8. JVM总结(四):JVM类加载机制

    这一节我们来总结一下JVM类加载机制.具体目录如下: 类加载的过程 类加载过程概括 说说引用 详解类加载全过程: 加载 验证 准备 解析 初始化 虚拟机把描述类的数据从Class文件加载到内存,并对数 ...

  9. Java基础-类加载机制与自定义类Java类加载器(ClassLoader)

    Java基础-类加载机制与自定义类Java类加载器(ClassLoader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于类加载器的概念和分类我就不再废话了,因为我在之前的笔 ...

  10. JVM 类加载机制详解

    如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lan ...

随机推荐

  1. Python中文分词组件 jieba

    jieba "结巴"中文分词:做最好的Python中文分词组件 "Jieba" Feature 支持三种分词模式: 精确模式,试图将句子最精确地切开,适合文本分 ...

  2. webpack整合 .vue 文件,集成 vue-loader

    webpack集成vue-loader 创建一个文件夹 test_webpack_vue 在 test_webpack_vue 下新建目录 src 在 src 目录下 新建文件 index.html ...

  3. VS2012隐藏输出窗口的快捷键是什么。

    纯属用键盘无法直接关闭这个窗口.有一个变通的方法是,先切换到这个输出窗口(标题呈现高亮的蓝色),使用Alt+W打开窗口菜单,选H隐藏就可以关闭.使用Ctrl+Alt+o可再次打开.按ESC就可以了.我 ...

  4. innodb存储引擎之内存

    1.innoDB存储引擎体系架构 如上图所示,innoDB存储是基于磁盘存储的,并且其中的记录以页的方式进行管理,但为什么要引入一个内存池呢? 其目的就是为了协调CUP速度与磁盘速度的鸿沟,基于磁盘的 ...

  5. GraphX介绍

    转自:https://www.cnblogs.com/txq157/p/5978747.html 1.GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理框架,它 ...

  6. Delphi 使用数据库浏览器

    樊伟胜

  7. c++ 一些注意事项

    1.long int的字节信息:int在32位系统下是4字节,long在32位也是4字节,在64位Int不变,但是long变成8字节,所以我们的编译器不同可能会导致我们处理int,long不同 2.注 ...

  8. VMware提示此主机支持Intel VT-x,但Intel VT-x处于禁用状态怎么解决

    本文链接:https://blog.csdn.net/weixin_40816738/article/details/90146770 ThinkPad笔记本1.开机按F1或Fn+F1进入BIOS,切 ...

  9. 转 SQL连接查询语句(内、外、交叉和合并查询)

    转 http://blog.csdn.net/u010011371/article/details/50596535 1.内连接 (INNER JOIN) 内连接也称自然连接,它是根据两个或多个表中的 ...

  10. H265码流格式

    一.H265码流格式 VPS:视频参数集,用于传输视频分级信息,有利于兼容标准在可分级视频编码或多视点视频的扩展. NALU header定义: NALU header(){ Descriptor f ...