JVM类加载机制和双亲委派

2024-06-05 18:04

本文主要是介绍JVM类加载机制和双亲委派,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

类加载机制

java文件需要编译成字节码文件(.class文件),jvm是通过类加载机制,将.class文件加载进内存,经过验证连接->初始化直到使用该对象的过程就是类加载机制,当new对象的时候,jvm首先去常量池寻找该类的符号引用,找不到此引用,则执行类加载,简而言之就是jvm通过类加载器加载.class文件变成对象的过程就是类加载机制

三个重要的内置ClassLoader 

  • BootstrapClassLoader(启动类加载器(根)加载器) 负责加载\lib下的类库加载进内存,用来加载java的核心库
  • ExtensionClassLoader (扩展类加载器) 负责加载lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类
  • AppClassLoader (应用类加载器) 加载 Classpath 环境变量里定义的路径中的 jar 包和目录,继承自ClassLoader抽象类,所以自定义加载器也需要继承此接口,并重写findClass方法
 protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}

自定义类加载器

自定义加载器继承自ClassLoader,重写findClass方法

package com.alibaba.fescar.core.protocol.test;import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;public class MyDefineClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {// 读取字节数据Path path = Paths.get("D:\\601\\acm601\\cldm_springcloud\\wsd-common\\src\\main\\java\\com\\alibaba\\fescar\\core\\protocol\\test\\TestClass.class");byte[] classData = Files.readAllBytes(path);// 将字节码内容转换为Class对象return defineClass(name, classData, 0, classData.length);} catch (IOException e) {throw new ClassNotFoundException("Class not found: " + name, e);}}
}

定义测试类,并生成.class文件

package com.alibaba.fescar.core.protocol.test;public class TestClass {public void testClassLoader(){System.out.println("test my define classloader");}
}

自定义类加载器的使用 

package com.alibaba.fescar.core.protocol.test;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Test {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {MyDefineClassLoader myDefineClassLoader = new MyDefineClassLoader();// 加载测试类生成Class对象 一定要带包名Class<?> testClass = myDefineClassLoader.loadClass("com.alibaba.fescar.core.protocol.test.TestClass");// 使用反射获得对象Object o =testClass.getDeclaredConstructor().newInstance();Method testClassLoader = testClass.getMethod("testClassLoader");// 调用方法testClassLoader.invoke(o);}
}

运行结果如下 

类加载的过程 

  • 加载阶段(将.class文件加载进内存)
  • 验证、准备、解析阶段(验证.class文件的正确性 准备(为类变量(静态变量)分配内存和初始化默认值)解析(将常量池池中的符号引用转化为直接引用))
  • 初始化阶段(执行类构造器init)

加载阶段 

 将.class字节码文件的二进制数据读入内存中,然后将这些数据翻译成类的元数据,元数据包括方法代码,变量名,方法名,访问权限与返回值,接着将元数据存入方法区,最后会在堆中创建一个Class对象

.class文件读入内存——>元数据放进方法区——>Class对象放进堆中

验证、准备、解析阶段 

验证被加载类的正确性与安全性,看class文件是否正确,是否对会对虚拟机造成安全问题等,主要去验证文件格式与符号引用等

对整个类加载机制而言,验证阶段是一个很重要但是非必需的阶段,毕竟验证需要花费一定的的时间,可以使用-Xverfity:none来关闭大部分的验证

准备 在这个阶段中,主要是为类变量(静态变量)分配内存以及初始化默认值,因为静态变量全局只有一份,是跟着类走的,因此分配内存其实是在方法区上分配。

  • 在准备阶段,虚拟机只为静态变量分配内存,实例变量要等到初始化阶段才开始分配内存
  • 为静态变量初始化默认值,是初始化对应数据类型的默认值,不是自定义的值
  • 被final修饰的静态变量,如果值比较小,则在编译后直接内嵌到字节码中。如果值比较大,也是在编译后直接放入常量池中。准备阶段结束后,final类型的静态变量已经有了用户自定义的值,而不是默认值

 解析阶段,主要是将class文件中常量池中的符号引用转化为直接引用

 符号引用:可以直接理解为是一个字符串,用这个字符串来表示一个目标

 直接引用:直接引用是一个指向目标的指针,能够通过直接引用定位到目标

Logger logger = new Logger();

我们可以通过引用变量logger直接定位到新创建出的Logger 对象实例,将符号引用转化为直接引用,就能将字符串logger转化为指向对象的指针

 初始化阶段

初始化,就是虚拟机执行类构造器<clinit>方法的过程,<clinit>方法是由编译器自动去搜集类中的所有类变量与静态语句块合并产生的。可能存在多个线程同时执行某个类的<clinit>()方法,虚拟机此时会对该方法进行加锁,保证只有一个线程能执行

在此阶段类变量与类成员变量才会被赋予用户自定义的值,只有在初始化阶段完成后,类才能被正常使用

初始化顺序 

父类的静态域->子类的静态域->父类的非静态域->子类的非静态域->父类的构造方法->子类的构造方法

静态域包括静态变量与静态代码块,静态变量和静态代码块的执行顺序由编码顺序决定

静态先于非静态,父类先于子类,构造方法在最后

双亲委派机制

java虚拟机中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题,针对的是类加载器(ClassLoader),避免了类的重复加载

当一个类加载器收到了一个类加载请求时,它自己不会先去尝试加载这个类,而是把这个请求转交给父类加载器,每一个层的类加载器都是如此,因此所有的类加载请求都应该传递到最顶层的启动类加载器中。只有当父类加载器在自己的加载范围内没有搜寻到该类时,并向子类反馈自己无法加载后,子类加载器才会尝试自己去加载

如果一个类重复出现在三个类加载器的加载位置,应该由启动类加载器(根加载器)加载,因为根据双亲委派机制,它的优先级是最高的 

打破双亲委派 

打破双亲委派机制的主要原因是为了满足一些特定的需求和场景:

  • 实现类的热部署:在某些应用场景下,需要在运行时动态加载和替换类,以实现热部署的功能。而双亲委派机制会导致类的加载只发生一次,无法实现类的热替换
  • 加载非标准的类文件:有些特殊的类文件,如动态生成的字节码、非标准的类文件格式等,无法通过标准的类加载器加载
  • 实现类加载的动态控制:有些应用需要对类的加载进行特殊的控制,例如对特定的类进行加密、解密或验证等操作

打破双亲委派的方法 

自定义类加载器 通过自定义ClassLoader的子类,重写findClass()方法,实现自定义的类加载逻辑,不委托给父类加载器,从而打破双亲委派机制

使用Java动态代理 Java动态代理机制可以在运行时生成代理类,并在代理类中实现特定的逻辑。通过使用动态代理,可以在类加载时动态生成代理类,从而打破双亲委派机制

线程上下文类加载器通过Thread类的setContextClassLoader()方法,可以设置线程的上下文类加载器,从而打破双亲委派机制

当然还有其他方法和框架打破双亲委派,比如OSGi框架、动态代理框架等

这篇关于JVM类加载机制和双亲委派的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1033776

相关文章

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2