当前位置: 首页 > news >正文

1.JVM常识之 类加载器

1.jvm组成

在这里插入图片描述
JVM组成:
1.类加载器
2.运行时数据区
3.执行引擎
4.本地库接口

各组件的作用:
首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

2.类加载器

下图借用图灵 诸葛老师的流程图,respect!!!
在这里插入图片描述
2.1 java里的几种类加载器

BootstrapClassLoad 系统级类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库。是C或者C++ 生成的对象。创建了Launcher类
extClassloader 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的Jar类包。属于Launcher类构造方法生成
AppClassload应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载开发自己写的那些类。属于Launcher类构造方法生成
也可以自定义加载器:负责加载开发者自定义路径下的类包。

Launcher 类部分源码
在这里插入图片描述

package sun.misc;public class Launcher {private static Launcher launcher = new Launcher();private static String bootClassPath = System.getProperty("sun.boot.class.path");private ClassLoader loader;public static Launcher getLauncher() {return launcher;}public Launcher() {ExtClassLoader var1;try {//extClassloader 扩展类加载器 获取,在getExtClassLoader()里没有ExtClassLoader 的话,会new ExtClassLoader()var1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {//AppClassload应用程序类加载器获取,在getAppClassLoader()里没有AppClassLoader的话,会new AppClassLoader()。AppClassLoader是应用级默认的类加载器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}}
}

2.2类加载过程
注:主类在运行过程中如果使用到其它类,会逐步加载这些类。jar包或war包里的类不是一次性全部加载的,是使用到时才加载。懒加载模式。
主要流程:
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载

加载:根据类文件路径找到相应的class文件导入
检测:校验字节码class文件的正确性。都是cafe babe 开头的字节码文件。
准备: 给类中的静态变量分配内存空间,并赋予默认值 eg:boolean false int 0
解析:jvm将常量池中的符号引用替换成直接引用的过程
初始化:对静态变量和静态代码块执行初始化工作。

2.3ClassLoader 类加载过程源码
类加载过程都在根加载器抽象类:ClassLoader 里有定义或者实现
该ClassLoader 类有两个核心方法
loadClass(String, boolean),实现了双亲委派机制,
还有一个方法是findClass(),默认实现是空方法。根据要加载的class类路径去实际加载类

package java.lang;
public abstract class ClassLoader {private final ClassLoader parent;/*** 双亲委派加载方式的保证*/protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded//校验当前类对象是否已经被加载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();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;}}//由子类实现的实际加载类的操作 实际实现是URLClassLoader类protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}
}

URLClassLoader类 是AppClassLoader 和 ExtClassLoader 的父类。
Launcher类中的静态内部类AppClassLoader部分核心源码

 static class AppClassLoader extends URLClassLoader {final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {//AppClassLoader 加载的是应用ClassPath路径下的类包final String var1 = System.getProperty("java.class.path");final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {public AppClassLoader run() {URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);return new AppClassLoader(var1x, var0);}});}//实际加载类调用的是父类URLClassLoader类中的loadClasspublic Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {int var3 = var1.lastIndexOf(46);if (var3 != -1) {SecurityManager var4 = System.getSecurityManager();if (var4 != null) {var4.checkPackageAccess(var1.substring(0, var3));}}if (this.ucp.knownToNotExist(var1)) {Class var5 = this.findLoadedClass(var1);if (var5 != null) {if (var2) {this.resolveClass(var5);}return var5;} else {throw new ClassNotFoundException(var1);}} else {//调用的是父类URLClassLoader类中的loadClassreturn super.loadClass(var1, var2);}}}

Launcher类中的静态内部类ExtClassLoader部分核心源码

static class ExtClassLoader extends URLClassLoader {private static volatile ExtClassLoader instance;public static ExtClassLoader getExtClassLoader() throws IOException {if (instance == null) {Class var0 = ExtClassLoader.class;synchronized(ExtClassLoader.class) {if (instance == null) {instance = createExtClassLoader();}}}return instance;}private static ExtClassLoader createExtClassLoader() throws IOException {try {return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {public ExtClassLoader run() throws IOException {File[] var1 = Launcher.ExtClassLoader.getExtDirs();int var2 = var1.length;for(int var3 = 0; var3 < var2; ++var3) {MetaIndex.registerDirectory(var1[var3]);}return new ExtClassLoader(var1);}});} catch (PrivilegedActionException var1) {throw (IOException)var1.getException();}}void addExtURL(URL var1) {super.addURL(var1);}public ExtClassLoader(File[] var1) throws IOException {super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);}private static File[] getExtDirs() {//这里可以看出拓展类加载器 加载的是jre中ext内的相关类String var0 = System.getProperty("java.ext.dirs");File[] var1;if (var0 != null) {StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);int var3 = var2.countTokens();var1 = new File[var3];for(int var4 = 0; var4 < var3; ++var4) {var1[var4] = new File(var2.nextToken());}} else {var1 = new File[0];}return var1;}}

总结:
ClassLoad 抽象类里面2个核心方法:
loadClass()实现了双亲委派机制
findClass() 根据对应的类路径加载类

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

在这里插入图片描述
双亲委派:加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

3.1为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改,安全性保证。
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性。

3.2自定义类加载器(父加载器是appClassLoad)
自定义类加载器只需要继承 java.lang.ClassLoader 抽象类,重写该类有两个核心方法,
loadClass(),实现了双亲委派机制,findClass(),默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。

3.3 自定义类加载器打破双亲委派机制
重写继承 java.lang.ClassLoader 抽象类中的loadClass()即可

http://www.lryc.cn/news/38706.html

相关文章:

  • 一天搞定《AI工程师的PySide2 PyQt5实战开发手册》
  • 身份推理桌游
  • [LeetCode周赛复盘] 第 99 场双周赛20230304
  • Parcel Bundle漏洞学习
  • RTP载荷H264(实战细节)
  • 软考高级信息系统项目管理师系列之四十三:信息系统安全管理
  • 并发编程之AtomicUnsafe
  • GDB调试快速入门
  • Vim一次复制,多次粘贴
  • 如何修改Win11上的默认程序?
  • 安装Linux虚拟机和Hadoop平台教程汇总及踩坑总结
  • Shell脚本的使用和介绍
  • 机械学习 - 基础概念 - scikit-learn - 数据预处理 - 1
  • OLCNE cluster 配置 NFS Storage(英文)
  • RabbitMQ高级特性
  • 利用Dockerfile开发定制镜像实战.
  • PyInstaller 将DLL文件打包进exe
  • 【JVM篇2】垃圾回收机制
  • LeetCode598. 范围求和 II(python)
  • 观察者模式与发布订阅模式
  • 磨金石教育摄影技能干货分享|烟花三月下扬州,是时候安排了!
  • Kafka 消费组位移
  • Python|数学|贪心|数组|动态规划|单选记录:实现保留3位有效数字(四舍六入五成双规则)|用Python来创造一个提示用户输入数字的乘法表|最小路径和
  • 【MySQL】MySQL的索引
  • 弱监督实例分割 Box-supervised Instance Segmentation with Level Set Evolution 论文笔记
  • Springboot是什么
  • LeetCode 134. 加油站(函数图像法 / 贪心)
  • 王道计算机组成原理课代表 - 考研计算机 第三章 存储系统 究极精华总结笔记
  • Flask-mock接口数据流程
  • springboot项目配置序列化,反序列化器