JVM虚拟机详解(三)类加载器的分类

1. 类加载器概述

  1. JVM严格来讲支持两种类型的类加载器 。分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
  2. 从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
  3. 无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个,如下所示

这里的四者之间是包含关系,不是上层和下层,也不是子系统的继承关系。

  • 我们通过一个类,获取它不同的加载器

    package com.peppa.classloader;/*** @author peppa* @create 2022-02-10 11:25:46*/
    public class ClassLoaderTest {public static void main(String[] args) {// 获取系统类加载器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);// 获取其上层的:扩展类加载器ClassLoader extClassLoader = systemClassLoader.getParent();System.out.println(extClassLoader);// 试图获取 根加载器ClassLoader bootstrapClassLoader = extClassLoader.getParent();System.out.println(bootstrapClassLoader);// 获取自定义加载器ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader);// 获取String类型的加载器ClassLoader classLoader1 = String.class.getClassLoader();System.out.println(classLoader1);}
    }// 执行结果:
    sun.misc.Launcher$AppClassLoader@18b4aac2
    sun.misc.Launcher$ExtClassLoader@1b6d3586
    null
    sun.misc.Launcher$AppClassLoader@18b4aac2
    null
    

    得到的结果,从结果可以看出 根加载器无法直接通过代码获取,同时目前用户代码所使用的加载器为系统类加载器。同时我们通过获取String类型的加载器,发现是null,那么说明String类型是通过根加载器进行加载的,也就是说Java的核心类库都是使用根加载器进行加载的。

    我们尝试获取引导类加载器,获取到的值为 null ,这并不代表引导类加载器不存在,因为引导类加载器右 C/C++ 语言,我们获取不到

    两次获取系统类加载器的值都相同:sun.misc.Launcher$AppClassLoader@18b4aac2 ,这说明系统类加载器是全局唯一的

2.虚拟机自带的加载器

  • 启动类加载器(引导类加载器,Bootstrap ClassLoader)

    • 这个类加载使用C/C++语言实现的,嵌套在JVM内部。
    • 它用来加载Java的核心库(JAVAHOME/jre/1ib/rt.jar、resources.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类
    • 并不继承自ava.lang.ClassLoader,没有父加载器。
    • 加载扩展类和应用程序类加载器,并指定为他们的父类加载器。
    • 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
  • 扩展类加载器(Extension ClassLoader)

    • Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
    • 派生于ClassLoader类
    • 父类加载器为启动类加载器
    • 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/1ib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
  • 应用程序类加载器(系统类加载器,AppClassLoader)

    • javI语言编写,由sun.misc.LaunchersAppClassLoader实现
    • 派生于ClassLoader类
    • 父类加载器为扩展类加载器
    • 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
    • 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
    • 通过classLoader#getSystemclassLoader()方法可以获取到该类加载器
  • 用户自定义类加载器

    在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定制类的加载方式。 为什么要自定义类加载器?

    • 隔离加载类
    • 修改类加载的方式
    • 扩展加载源
    • 防止源码泄漏

    用户自定义类加载器实现步骤:

    • 开发人员可以通过继承抽象类ava.1ang.ClassLoader类的方式,实现自己的类加载器,以满足一些特殊的需求
    • 在JDK1.2之前,在自定义类加载器时,总会去继承ClassLoader类并重写1oadClass()方法,从而实现自定义的类加载类,但是在JDK1.2之后已不再建议用户去覆盖1oadclass()方法,而是建议把自定义的类加载逻辑写在findclass()方法中
    • 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URIClassLoader类,这样就可以避免自己去编写findclass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。

3.查看根加载器所能加载的目录

刚刚我们通过概念了解到了,根加载器只能够加载 java /lib目录下的class,我们通过下面代码验证一下

package com.peppa.classloader;import java.net.URL;
import java.security.Provider;/*** @author peppa* @create 2022-02-10 11:42:4*/
public class ClassLoaderPathTest {public static void main(String[] args) {System.out.println("*********启动类加载器************");// 获取BootstrapClassLoader 能够加载的API的路径URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();for (URL url : urls) {System.out.println(url.toExternalForm());}// 从上面路径中,随意选择一个类,来看看他的类加载器是什么:得到的是null,说明是  根加载器ClassLoader classLoader = Provider.class.getClassLoader();System.out.println("classLoader ==>"+classLoader);}
}// 得到的结果
*********启动类加载器************
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/resources.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/rt.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/sunrsasign.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/jsse.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/jce.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/charsets.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/lib/jfr.jar
file:/D:/software/Java/jdk1.8/jdk1.8.0_241/jre/classes
classLoader ==>null

4. 关于ClassLoader

  • ClassLoader 类介绍

    ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)

  • sun.misc.Launcher 它是一个java虚拟机的入口应用

  • 获取ClassLoader的途径

    • 获取当前ClassLoader:clazz.getClassLoader()
    • 获取当前线程上下文的ClassLoader:Thread.currentThread().getContextClassLoader()
    • 获取系统的ClassLoader:ClassLoader.getSystemClassLoader()
    • 获取调用者的ClassLoader:DriverManager.getCallerClassLoader()

5.双亲委派机制

Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
  3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
  4. 父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常

  • 双亲委派机制举例

    当我们加载jdbc.jar 用于实现数据库连接的时候,首先我们需要知道的是 jdbc.jar是基于SPI接口进行实现的,所以在加载的时候,会进行双亲委派,最终从根加载器中加载 SPI核心类,然后在加载SPI接口类,接着在进行反向委派,通过线程上下文类加载器进行实现类 jdbc.jar的加载。

  • 沙箱安全机制

    自定义string类,但是在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java\lang\String.class),报错信息说没有main方法,就是因为加载的是rt.jar包中的string类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制。

  • 双亲委派机制的优势

    通过上面的例子,我们可以知道,双亲机制可以

    • 避免类的重复加载
    • 保护程序安全,防止核心API被随意篡改
      • 自定义类:java.lang.String
      • 自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)
  • 其他

    • 如何判断两个class对象是否相同?

      在JVM中表示两个class对象是否为同一个类存在两个必要条件:

      1. 类的完整类名必须一致,包括包名
      2. 加载这个类的ClassLoader(指ClassLoader实例对象)必须相同
      3. 换句话说,在JVM中,即使这两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的
    • 对类加载器的引用

      1. JVM必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的
      2. 如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中

JVM虚拟机详解(三)类加载器的分类相关推荐

  1. JVM 虚拟机详解内部原理(小白必看!)

    前言 作为一名Java软件开发程序猿,不了解JVM?那么你就只能干CRUD的工作! 前几天刚学习了JVM,把学习到的经验在这里和大家分享下,有啥意见,欢迎在下方评论交流! 1.什么是JVM? JVM全 ...

  2. java classloader详解_Java类加载器(ClassLoader)详解

    本文主要讲述Java ClassLoader的工作原理,这为后面将Android App代码热替换或者插件化升级做铺垫 一. 类加载器 ClassLoader即常说的类加载器,其功能是用于从Class ...

  3. Java中JVM虚拟机详解

    1. 什么是JVM? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来 ...

  4. OkHttp3源码详解(三) 拦截器-RetryAndFollowUpInterceptor

    最大恢复追逐次数: private static final int MAX_FOLLOW_UPS = 20; 处理的业务: 实例化StreamAllocation,初始化一个Socket连接对象,获 ...

  5. Java虚拟机详解----JVM常见问题总结

    [正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾 ...

  6. JVM内幕:Java虚拟机详解

    这篇文章解释了Java 虚拟机(JVM)的内部架构.下图显示了遵守 Java SE 7 规范的典型的 JVM 核心内部组件. 上图显示的组件分两个章节解释.第一章讨论针对每个线程创建的组件,第二章节讨 ...

  7. JVM专题(2)-类加载器子系统

    目前博主个人博客已经搭建发布,后期相关文章也会发布在上面,大家有兴趣可以去上面学习,点击即可前往文青乐园 1.内存结构概述 假设我们想自己手写一个Java虚拟机的话,必须考虑以下结构: 类加载器 执行 ...

  8. PackageManagerService启动详解(三)之开始初始化阶段流程分析

      PKMS启动详解(三)之BOOT_PROGRESS_PMS_START阶段流程分析 Android PackageManagerService系列博客目录: PKMS启动详解系列博客概要 PKMS ...

  9. 深入理解JVM(1):类加载器

    文章目录 一.类加载简介 1.简介 2.Java虚拟机与程序的生命周期 3.类的加载.连接与初始化(类加载的最重要的3个阶段) 3.1加载 3.2连接 3.3 初始化 4.类的使用和卸载(类加载的剩余 ...

最新文章

  1. 3ds Max V-Ray5 完整指南大师班视频教程
  2. JavaScript去除字符串首尾空格
  3. 模糊控制算法详细讲解
  4. IT人:如何预防久坐伤身?
  5. BrainOS —最像大脑的AI
  6. 中国1km分辨率的DEM数据以及合并后的中国行政区划数据
  7. tortoise清理本地分支_如何删除TortoiseHg中的意外分支?
  8. 穿西服和穿皮鞋有那些讲究?
  9. 关于商业企业创业的思考
  10. 人工智能产业盛宴:2019 AIIA开发者大会即将揭幕
  11. 电影《战狼2》的可视化分析
  12. win10显示桌面计算机图标怎么删除,怎么设置显示或隐藏win10系统桌面上的我的电脑图标...
  13. file.getOriginalFilename()
  14. Pytest如何并发执行自动化脚本
  15. python+openCV 自适应阈值分割
  16. 华为机试真题 C++ 实现【模拟商场优惠打折】【2022.11 Q4 新题】
  17. 欧美是怎么做创新的?
  18. 《C程序设计快速进阶大学教程》第8章编程题
  19. 邓亚萍大手笔一掷20亿研发即刻搜索2年就倒闭带来的思考
  20. OpenLDAP安装使用及与各系统的集成

热门文章

  1. SpringBoot 中使用 JWT 案例分享详解
  2. 【23考研】计算机择校信息库-湖北高校计算机相关专业22专业目录分类汇总(按专业课分类汇总)
  3. 【小迪安全】Day16web漏洞-SQL注入之盲注
  4. 软件工程毕设新颖课题
  5. 山东大学再次采购ZJ-3型精密压电D33测试仪
  6. vscode 主题
  7. DHCP Snooping实验
  8. 【PA2014】【BZOJ3719】Plemiona
  9. 吸金千万的资金盘Fomo3D凉了,这些模仿他的却火了。
  10. 如何利用linux分析转录组数据库,转录组分析(8)----批量处理脚本了解一下