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

相关文章

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Spring MVC如何设置响应

《SpringMVC如何设置响应》本文介绍了如何在Spring框架中设置响应,并通过不同的注解返回静态页面、HTML片段和JSON数据,此外,还讲解了如何设置响应的状态码和Header... 目录1. 返回静态页面1.1 Spring 默认扫描路径1.2 @RestController2. 返回 html2

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.