目录

  • 1. 什么是类加载器
  • 2. 类加载器加载的过程
  • 3. Class文件读取来源
  • 4. 类加载器的分类
  • 5. 那些操作会初始化类加载器
  • 6. 类加载器的双亲委派机制
    • 6.1 双亲委派机制机制的好处
  • 7. ClassLoader源码解读
    • 7.1 Launcher类源码解读
    • 7.2 双亲委派机制源码分析
    • 7.3 如何自定义一个类加载器
    • 7.4 自定义类加载器
    • 7.5 根据类加载器手写热部署插件
    • 7.6 什么是SPI机制
    • 7.7 如何绕开双亲委派原则
  • 8. 常见Java虚拟机
  • 9. 常见的几款java虚拟机

1. 什么是类加载器

将我们的class文件读取到内存中

2. 类加载器加载的过程

类加载器加载我们的class文件,并且经历过验证、准备、解析,在初始化我们该类。

3. Class文件读取来源

1.本地磁盘文件 java源代码编译的class文件
2.通过网络下载的class文件
3.War、Jar解压的class文件
4.从专门的数据库中读取的class文件
5.使用java cglib、动态代理生成的代理类class文件
Jvm虚拟机中 通过 类加载器(用户可以自定义类加载器)

4. 类加载器的分类

1.启动(Bootstrap)类加载器:加载JVM自身工作需要的类,它由JVM自己实现。它会加载 J A V A H O M E / j r e / l i b 下 的 文 件 底 层 是 C 语 言 实 现 2. 扩 展 ( E x t e n s i o n ) 类 加 载 器 : 它 是 J V M 的 一 部 分 , 由 s u n . m i s c . L a u n c h e r E x t C l a s s L o a d e r 实 现 , 他 会 加 载 E x t C l a s s L o a d e r 实 现 , 他 会 加 载 E x t C l a s s L o a d e r 实 现 , 他 会 加 载 J A V A H O M E / j r e / l i b / e x t 目 录 中 的 文 件 ( 或 由 S y s t e m . g e t P r o p e r t y ( “ j a v a . e x t . d i r s ” ) 所 指 定 的 文 件 ) 。 底 层 是 J a v a 实 现 3. ( 应 用 ) A p p C l a s s L o a d e r 类 加 载 器 : 应 用 类 加 载 器 , 我 们 工 作 中 接 触 最 多 的 也 是 这 个 类 加 载 器 , 它 由 s u n . m i s c . L a u n c h e r JAVA_HOME/jre/lib下的文件 底层是C语言实现 2.扩展(Extension)类加载器:它是JVM的一部分,由sun.misc.LauncherExtClassLoader实现,他会加载ExtClassLoader实现,他会加载ExtClassLoader实现,他会加载JAVA_HOME/jre/lib/ext目录中的文件(或由System.getProperty(“java.ext.dirs”)所指定的文件)。 底层是Java实现 3.(应用)AppClassLoader 类加载器:应用类加载器,我们工作中接触最多的也是这个类加载器,它由sun.misc.Launcher JAVAH​OME/jre/lib下的文件底层是C语言实现2.扩展(Extension)类加载器:它是JVM的一部分,由sun.misc.LauncherExtClassLoader实现,他会加载ExtClassLoader实现,他会加载ExtClassLoader实现,他会加载JAVAH​OME/jre/lib/ext目录中的文件(或由System.getProperty(“java.ext.dirs”)所指定的文件)。底层是Java实现3.(应用)AppClassLoader类加载器:应用类加载器,我们工作中接触最多的也是这个类加载器,它由sun.misc.LauncherAppClassLoader实现。他加载我们工程目录classpath下的class及jar包 底层是java实现
4.自定义类加载器: 也就是用户自己定义的类加载器

Launcher 源码解读

Launcher.ExtClassLoader var1;
try {// 获取到我们扩展类加载器var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);
}try {//获取到我们应用类加载器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);
}
// 当前程序启动的线程 默认的 ClassLoader 应用类加载器
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {SecurityManager var3 = null;if (!"".equals(var2) && !"default".equals(var2)) {try {var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();} catch (IllegalAccessException var5) {} catch (InstantiationException var6) {} catch (ClassNotFoundException var7) {} catch (ClassCastException var8) {}} else {var3 = new SecurityManager();}if (var3 == null) {throw new InternalError("Could not create SecurityManager: " + var2);}System.setSecurityManager(var3);

5. 那些操作会初始化类加载器

类的主动使用:

  1. 调用类的静态方法
  2. invokeStatic 调用静态方法
  3. Main
  4. New
  5. Class.formname
  6. 子类初始化一定会初始化父类
    初始化一个类,那么一定会触发类加载器
    但是类加载器加载了该类,但是该类不一定初始化。

6. 类加载器的双亲委派机制

首先在我们类加载器分为四种 自定义类加载器、应用类加载器、扩展类加载器、启动类加载器。
当一个类加载器收到请求之后,首先会依次向上查找到最顶层类加载器(启动类加载器),依次向下加载class文件,如果已经加载到class文件,子加载器不会加继续加载该class文件。

6.1 双亲委派机制机制的好处

目的就是为了防御开发者为定义的类与jdk定义源码类产生冲突问题,保证该类在内存中的唯一性。

7. ClassLoader源码解读

7.1 Launcher类源码解读

public Launcher() {Launcher.ExtClassLoader var1;try {//获取我们的扩展类加载器var1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {// 获取我们的应用类加载器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}// 默认设置我们的类加载器是为应用类加载器Thread.currentThread().setContextClassLoader(this.loader);String var2 = System.getProperty("java.security.manager");if (var2 != null) {SecurityManager var3 = null;if (!"".equals(var2) && !"default".equals(var2)) {try {var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();} catch (IllegalAccessException var5) {} catch (InstantiationException var6) {} catch (ClassNotFoundException var7) {} catch (ClassCastException var8) {}} else {var3 = new SecurityManager();}if (var3 == null) {throw new InternalError("Could not create SecurityManager: " + var2);}System.setSecurityManager(var3);}}

7.2 双亲委派机制源码分析

  1. ClassLoader.getSystemClassLoader().loadClass()
// 查询缓存中是否有缓存 该class
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();
// 如果父加载器(扩展和启动类加载器都没有加载class,则使用当前(应用类加载器加载))c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}
}
if (resolve) {resolveClass(c);
}
return c;

7.3 如何自定义一个类加载器

public class DemoClassLoader extends ClassLoader {private File fileObject;public DemoClassLoader(File fileObject) {this.fileObject = fileObject;}public void setFileObject(File fileObject) {this.fileObject = fileObject;}public File getFileObject() {return fileObject;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = getClassFileBytes(this.fileObject);return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();return null;}}/*** 从文件中读取去class文件** @throws Exception*/private byte[] getClassFileBytes(File file) throws Exception {//采用NIO读取FileInputStream fis = new FileInputStream(file);FileChannel fileC = fis.getChannel();ByteArrayOutputStream baos = new ByteArrayOutputStream();WritableByteChannel outC = Channels.newChannel(baos);ByteBuffer buffer = ByteBuffer.allocateDirect(1024);while (true) {int i = fileC.read(buffer);if (i == 0 || i == -1) {break;}buffer.flip();outC.write(buffer);buffer.clear();}fis.close();return baos.toByteArray();}}

代码测试:

Class<?> aClass = new DemoClassLoader(new File("D:\\code\\com\\demo\\DemoEntity.class")).loadClass("com.demo.DemoEntity");
Object o = aClass.newInstance();
System.out.println(o.getClass().getClassLoader());

7.4 自定义类加载器

ClassLoader 类加载器中 双亲委派机制 核心源码部分

findLoadedClass()— 首先,检查类是否已经加载
parent.loadClass(name, false); 读取到parent.loadClass

findBootstrapClassOrNull 使用启动类加载器读取
findClass 扩展和应用类加载器、自定义类加载器

public class DemoClassLoader extends ClassLoader {private File fileObject;public DemoClassLoader(File fileObject) {this.fileObject = fileObject;}public void setFileObject(File fileObject) {this.fileObject = fileObject;}public File getFileObject() {return fileObject;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = getClassFileBytes(this.fileObject);return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();return null;}}/*** 从文件中读取去class文件** @throws Exception*/private byte[] getClassFileBytes(File file) throws Exception {//采用NIO读取FileInputStream fis = new FileInputStream(file);FileChannel fileC = fis.getChannel();ByteArrayOutputStream baos = new ByteArrayOutputStream();WritableByteChannel outC = Channels.newChannel(baos);ByteBuffer buffer = ByteBuffer.allocateDirect(1024);while (true) {int i = fileC.read(buffer);if (i == 0 || i == -1) {break;}buffer.flip();outC.write(buffer);buffer.clear();}fis.close();return baos.toByteArray();}}

7.5 根据类加载器手写热部署插件

public class ClassFileEntity {/*** 类的名称*/private String name;/*** class*/private Class aClass;/*** 最后被更改的时间*/private long lastModified;public ClassFileEntity(String name, long lastModified) {this.name = name;this.lastModified = lastModified;}public ClassFileEntity(String name, long lastModified, Class aClass) {this.name = name;this.lastModified = lastModified;this.aClass = aClass;}public String getName() {return name;}public Class getaClass() {return aClass;}public long getLastModified() {return lastModified;}public void setName(String name) {this.name = name;}public void setaClass(Class aClass) {this.aClass = aClass;}public void setLastModified(long lastModified) {this.lastModified = lastModified;}
}
public class HotDeploymentPlug {//存放所有的class文件private Map<String, ClassFileEntity> mapClassFiles = new HashMap<>();private String path;/*** 包的名称*/private String packageName = "com.demo.";public HotDeploymentPlug(String path) {this.path = path;}public void start() {listener();}/*** 监听方法*/public void listener() {new Thread(() -> {while (true) {// 1.读取该文件下File files = new File(path);File[] tempList = files.listFiles();// 2.读取class文件 存入到 mapClassFilesfor (File file :tempList) {String name = file.getName();if (StringUtils.isEmpty(name)) {continue;}long l = file.lastModified();// 使用类加载器读取该 classString className = packageName + name.replace(".class", "");if (mapClassFiles.containsKey(className)) {// 则比对该class文件 是否被修改ClassFileEntity mapClassFileEntity = mapClassFiles.get(className);if (mapClassFileEntity.getLastModified() != l) {try {mapClassFileEntity.setLastModified(l);DemoClassLoader demoClassLoader = new DemoClassLoader(file);Class<?> aClass = demoClassLoader.loadClass(className);Object o = aClass.newInstance();log.info(className + "class文件发生了变化");} catch (Exception e) {log.error("e:{}", e);}}} else {ClassFileEntity newClassFileEntity = new ClassFileEntity(className, l);// 如果不存在 则存入到mapClassFiles集合中mapClassFiles.put(className, newClassFileEntity);}try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}public static void main(String[] args) {HotDeploymentPlug hotDeploymentPlug = new HotDeploymentPlug("D:\\code\\com\\demo");hotDeploymentPlug.listener();}
}

7.6 什么是SPI机制

Java SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制.
实现方式:

  1. 首先需要再resources目录下:创建文件夹META-INF services
  2. 定义接口文件的名称:
    D:\code\demo_jvm\src\main\resources\META-INF\services\com.demo.service.MyService
    名称规范:包名+类名组成。
com.demo.service.impl.MyServiceImpl01
com.demo.service.impl.MyServiceImpl02
ServiceLoader<MyService> load = ServiceLoader.load(MyService.class);
load.forEach((t) -> {System.out.println(t.get());
});

获取当前线程对应的应用类类加载器,加载该class。

ServiceLoader<MyService> load = ServiceLoader.load(MyService.class);
load.forEach((t) -> {System.out.println(t.get());
});

7.7 如何绕开双亲委派原则

//        Thread.currentThread().setContextClassLoader(Test02.class.getClassLoader().getParent());
//        Connection root =
//                DriverManager
//                        .getConnection
//                                (
//                                        "jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8",
//                                        "root", "root");
//        Class.forName("com.mysql.jdbc.Driver");ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {Driver next = driversIterator.next();System.out.println(next);}} catch(Throwable t) {// Do nothing}

8. 常见Java虚拟机

(1)HotSpot VM
HotSpot VM是目前主流的虚拟机。像Oracle / Sun JDK、OpenJDK的各种变种(例如IcedTea、Zulu),用的都是相同核心的HotSpot VM。从Java SE 7开始,HotSpot VM就是Java规范的“参考实现”,JDK8的HotSpot VM已经是以前的HotSpot VM与JRockit VM的合并版,也就是传说中的“HotRockit”,只是产品里名字还是叫HotSpot VM。这个合并并不是要把JRockit的部分代码插进HotSpot里,而是把前者一些有价值的功能在后者里重新实现一遍。移除PermGen、Java Flight Recorder、jcmd等都属于合并项目的一部分。
(2)J9 VM
J9是IBM开发的一个高度模块化的JVM。J9 VM的性能水平大致跟HotSpot VM是一个档次的。
(3)JRockit
以前Java SE的主流JVM中还有JRockit,跟HotSpot与J9一起并称三大主流JVM。这三家的性能水平基本都在一个水平上,竞争很激烈。自从Oracle把BEA和Sun都收购了之后,Java SE JVM只能二选一,JRockit就炮灰了。JRockit最后发布的大版本是R28,只到JDK6,原本在开发中的R29及JDK7的对应功能都没来得及完成项目就被终止了。

9. 常见的几款java虚拟机

SUN Classic VM:第一款商用java虚拟机,1996年1月jdk1.0中带的java虚拟机,只能使用纯解释器的方式来执行java代码
Exact VM:准确式内存管理,编译器和解释器混合工作以及两级即时编译 ,只在Solaris平台发布
HotSport VM:即时编译,节约了时间和存储,称霸武林
KVM:简单,轻量,高可以执行,主要在手机平台使用
JRockit:BEA,世界上最快的java虚拟机,专注服务端应用,优势:垃圾回收机制,MissionControl服务套件
j9:IBM  Technology for java virtual Machines IT4J
dalvik:不能直接指向class文件,寄存器架构,执行dex文件,由class文件转化而来
MicrosoftJvm:只能在windows平台运行,
高性能java虚拟机
  Azul VM:专用虚拟机,经HotSport改进得来,运行在本公司专有硬件中
  Liquid VM:不需要操作系统的支持
taobao虚拟机:淘宝深度定制的产品,硬件依赖性比较高

【JVM】Java类加载器设计原理(ClassLoader源码解读/ SPI机制/ 绕开双亲委派/ 常见Java虚拟机)相关推荐

  1. java计算机毕业设计Vue.js音乐播放器设计与实现源码+mysql数据库+系统+lw文档+部署

    java计算机毕业设计Vue.js音乐播放器设计与实现源码+mysql数据库+系统+lw文档+部署 java计算机毕业设计Vue.js音乐播放器设计与实现源码+mysql数据库+系统+lw文档+部署 ...

  2. 基于JAVA水果商城设计计算机毕业设计源码+数据库+lw文档+系统+部署

    基于JAVA水果商城设计计算机毕业设计源码+数据库+lw文档+系统+部署 基于JAVA水果商城设计计算机毕业设计源码+数据库+lw文档+系统+部署 本源码技术栈: 项目架构:B/S架构 开发语言:Ja ...

  3. 基于JAVA旅游网站设计计算机毕业设计源码+数据库+lw文档+系统+部署

    基于JAVA旅游网站设计计算机毕业设计源码+数据库+lw文档+系统+部署 基于JAVA旅游网站设计计算机毕业设计源码+数据库+lw文档+系统+部署 项目架构:B/S架构 开发语言:Java语言 开发软 ...

  4. java计算机毕业设计Vue.js音乐播放器设计与实现源码+数据库+系统+lw文档

    java计算机毕业设计Vue.js音乐播放器设计与实现源码+数据库+系统+lw文档 java计算机毕业设计Vue.js音乐播放器设计与实现源码+数据库+系统+lw文档 本源码技术栈: 项目架构:B/S ...

  5. 计算机毕业设计JavaVue.js音乐播放器设计与实现(源码+系统+mysql数据库+lw文档)

    计算机毕业设计JavaVue.js音乐播放器设计与实现(源码+系统+mysql数据库+lw文档) 计算机毕业设计JavaVue.js音乐播放器设计与实现(源码+系统+mysql数据库+lw文档) 本源 ...

  6. 基于java旅游网站设计计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署

    基于java旅游网站设计计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 基于java旅游网站设计计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署 本源码技术栈: 项目架构 ...

  7. 并发编程之 Executor 线程池原理与源码解读

    并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...

  8. Dubbo 源码分析 - SPI 机制

    1.简介 SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以 ...

  9. 史上最强 -- Java类加载器的原理及应用

    什么是classloader classloader顾名思义,即是类加载.虚拟机把描述类的数据从class字节码文件加载到内存,并对数据进行检验.转换解析和初始化,最终形成可以被虚拟机直接使用的Jav ...

最新文章

  1. 《The Sixth Sense》(《灵异第六感》)观后
  2. 蚂蚁金服开源增强版 Spring Boot 的研发框架!
  3. 推荐8个极受欢迎的网站和软件,让你总有一天你会用到!
  4. 如何快速测试与数据库的连接并得到连接字符串
  5. Android之SlidingMenu使用和总结
  6. cognito_将Amazon Cognito与单页面应用程序(Vue.js)集成
  7. 【运动快乐】享受赤脚慢跑 收获健康快乐
  8. 贪吃蛇python语言代码大全_Python编程代码:经典贪吃蛇
  9. Swift: 获取系统字体
  10. JAVA判断素数法+引用方法
  11. python离线录音转文字软件_有能把录音变成文字的软件么?
  12. 闲聊:Android 平台网络游戏加速器(二)
  13. cfree5c语言编写贪吃蛇,刚学C语言,想写一个贪吃蛇的代码
  14. 阿里云ECS服务器组内网
  15. OpenCVcv2.imread传入灰度图像,shape还是3通道
  16. 无刷新假象   实现简易文件上传
  17. SAP R3 系统技术基础
  18. golang:kratos生成proto以及API编写
  19. Excel如何根据分组插入空行
  20. Mobile TV Vs IPTV

热门文章

  1. 让你的app体验更丝滑的11种方法!冲击手机应用榜单Top3指日可待
  2. vscode关闭源代码管理处的提示
  3. 三维建模入门,从软件设置开始
  4. Python安装pycrypto出错处理方法
  5. 安卓文件名最大长度限制
  6. 基于paddlex图像分类模型训练(一):图像分类数据集切分:文件夹转化为imagenet训练格式
  7. 基于ssm+vue的医院门诊病历系统(源代码+数据库+11000字文档)626
  8. 奔走在算法的大路上(一)排序之归并排序
  9. 同花顺去年实现净利润6.3亿元 同比减少12.64%
  10. 一个人的风水【幸福与烦恼】