100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【JVM】Java类加载器设计原理(ClassLoader源码解读/ SPI机制/ 绕开双亲委派/ 常见Java虚拟机)

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

时间:2018-08-31 03:14:44

相关推荐

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

目录

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. 那些操作会初始化类加载器

类的主动使用:

调用类的静态方法invokeStatic 调用静态方法MainNewClass.formname子类初始化一定会初始化父类

初始化一个类,那么一定会触发类加载器

但是类加载器加载了该类,但是该类不一定初始化。

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 双亲委派机制源码分析

ClassLoader.getSystemClassLoader().loadClass()

// 查询缓存中是否有缓存 该classClass<?> 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,它可以用来启用框架扩展和替换组件。实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制.

实现方式:

首先需要再resources目录下:创建文件夹META-INF services定义接口文件的名称:

D:\code\demo_jvm\src\main\resources\META-INF\services\com.demo.service.MyService

名称规范:包名+类名组成。

com.demo.service.impl.MyServiceImpl01com.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:IBMTechnology for java virtual Machines IT4J

dalvik:不能直接指向class文件,寄存器架构,执行dex文件,由class文件转化而来

MicrosoftJvm:只能在windows平台运行,

高性能java虚拟机

Azul VM:专用虚拟机,经HotSport改进得来,运行在本公司专有硬件中

Liquid VM:不需要操作系统的支持

taobao虚拟机:淘宝深度定制的产品,硬件依赖性比较高

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。