类的生命周期中的第一步,就是要被 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. 判断一个数是否为回文数(js)

    //判断是否为回文数:若n=1234321,则称n为一回文数 let readline = require("readline-sync"); let newNum = 0; co ...

  2. 5、SVN 权限管理

    5.SVN 权限管理 5.1版本库中三个对应的配置文件 版本库配置文件目录 /var/svn/repository/pro_oa/conf svnserve.conf 文件,如下修改: 19# ano ...

  3. deep_learning_Function_tf.argmax()解析

    tf.argmax(input,axis)根据axis取值的不同返回每行或者每列最大值的索引. 这个很好理解,只是tf.argmax()的参数让人有些迷惑,比如,tf.argmax(array, 1) ...

  4. vim复制到剪切板

    作者:whinc链接:https://www.zhihu.com/question/19863631/answer/89354508来源:知乎 转载文章 Vim 中的复制.删除的内容都会被存放到默认( ...

  5. 小程序UI设计(9)-文字排版

    小程序中一般很少大段落的文字,基本是4.5个字的主题.一行标题.一行摘要.两行描述.显示时超出部分用省略号代替.下面结合工具使用介绍一下文字排版的方法.先看效果图.  工具中属性设置如下图:两行文字属 ...

  6. Beta冲刺——星期五

    这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 飞猪们 这个作业的目标 剩余任务预估,分配任务(开发,测试等).按要求提交当天冲刺报告. ...

  7. 腾讯数据安全专家谈联邦学习开源项目FATE:通往隐私保护理想未来的桥梁

    数据孤岛.数据隐私以及数据安全,是目前人工智能和云计算在大规模产业化应用过程中绕不开的“三座大山”. “联邦学习”作为新一代的人工智能算法,能在数据不出本地的情况下,实现共同建模,提升AI模型的效果, ...

  8. java 多线程,线程安全等定义

    线程安全, synchronized的使用,保证方法或代码块操作的原子性.可见性和有序性 参考这篇文章: 7张图带你轻松理解Java 线程安全 public class ThreadDemo { pr ...

  9. Summer training #4

    D:找到两个数 一个是另一个的整数倍(1也算) 因为N是600000 调和级数为ln(n+1) 算一下 可以直接爆 #include <bits/stdc++.h> #include &l ...

  10. GitHub 上最热的10款国产开源软件

    衡量一个开源产品好不好,看看产品在 GitHub 的 Star 数量就知道了.由此可见,GitHub 已经沦落为开源产品的“大众点评”了.一个开源产品希望快速的被开发者知道.快速的获取反馈,放到 Gi ...