11.《深入理解Java虚拟机》类加载器与双亲委派模型

2024-05-23 18:32

本文主要是介绍11.《深入理解Java虚拟机》类加载器与双亲委派模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2017-1-2 20:20 写了一天的blog和看书,看不下去了,回去健身。明天再写。
2017-1-2 23:00 回到寝室我还是妥协了,还是先把这个写完吧。

类与类加载器

虚拟机设计团队把类的加载阶段中的”通过一个类的全限定名来获取此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为”类加载器”。

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限定于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话表达地再简单一点就是:比较两个类是否”相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则即使这两个类来源于同一个.class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,这两个类必定不相等。

上面说的”相等”,包括代表类的.class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等情况。

下面给出了一个示例显示不同的类加载器对instanceof关键字运算结果的影响:

package JVM;import java.io.IOException;
import java.io.InputStream;/*** Created by louyuting on 17/1/2.*/
public class ClassLoaderTest {public static void main(String[] args) throws Exception{ClassLoader myLoader = new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {try {String fileName = name.substring(name.lastIndexOf(".")+1)+".class";InputStream is = getClass().getResourceAsStream(fileName);if(is == null){return super.loadClass(name);}byte[] b = new byte[is.available()];is.read(b);return defineClass(name, b, 0, b.length);} catch (IOException e) {throw new ClassNotFoundException(name);}}};Object obj = myLoader.loadClass("JVM.ClassLoaderTest").newInstance();System.out.println(obj.getClass());System.out.print(obj instanceof JVM.ClassLoaderTest);}
}

上述代码的运行结果是:
运行结果

代码里面构造了一个简单的类加载器,加载同一路径下面的class文件。我使用我自己定义的类加载器去加载JVM.ClassLoaderTest这个类,并实例化该对象。从输出结果可知,该类确实是JVM.ClassLoaderTest实例化出来的对象。但是从第二个false可知,该对象与类JVM.ClassLoaderTest做类型检查返回了false,这是因为虚拟机中存在了两个JVM.ClassLoaderTest类,一个是我自己定义的类加载器加载的,一个是系统应用程序类加载器加载的。虽然来自同一个class文件但是依然是两个独立的类,做对象所属类型检查时结果自然是false。

双亲委派模型

从虚拟机角度看只存在两种类型的类加载器:
1. 一种是启动类加载器(BootStrap ClassLoader),这个加载器使用C++实现,是虚拟机自身的一部分。
2. 另外一种就是所有其他类加载器,这些类加载器都是由java语言实现的,独立于虚拟机外部,并且全部继承与java.lang.ClassLoader。

从开发人员角度看可以划分更加细:
类加载器双亲委派模型

1、启动类加载器Bootstrap ClassLoader
之前说过了这是一个嵌在JVM内核中的加载器。它负责加载的是JAVA_HOME/lib下的类库,或则被-Xbootclasspath参数指定的路径中的,并且是虚拟机是别的类库加载到虚拟机内存中。系统类加载器无法被Java程序直接应用

2、扩展类加载器Extension ClassLoader
这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责用于加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量指定所指定的路径中所有类库,开发者可以直接使用扩展类加载器。java.ext.dirs系统变量所指定的路径的可以通过程序来查看
System.out.println(System.getProperty("java.ext.dirs"));
运行结果:
/Users/louyuting/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.7.0_72.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java

3、应用程序类加载器Application ClassLoader
这个类加载器由sun.misc.Launcher$AppClassLoader实现。这个类也一般被称为系统类加载器。

我们的应用程序都是由这3种类加载器互相配合进行加载的,有必要还可以加入自定义的类加载器。

下图展示了类加载器之间的层次关系:
双亲委派模型

这里的类加载器之间的父子关系不是继承关系,而只是层次上的定义。它并不是一个强制性的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式。

双亲委派模型的工作过程是:
1. 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。
2. 所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

所以,其实所有的加载请求最终都应该传送到顶层的启动类加载器中。双亲委派模型对于Java程序的稳定运作很重要,因为Java类随着它的加载器一起具备了一种带有优先级的层次关系。例如java.lang.Object,存放于rt.jar中,无论哪一个类加载器要去加载这个类,最终都是由Bootstrap ClassLoader去加载,因此Object类在程序的各种类加载器环境中都是一个类。相反,如果没有双亲委派模型,由各个类自己去加载的话,如果用户自己编写了一个java.lang.Object,并放在CLASSPATH下,那系统中将会出现多个不同的Object类,Java体系中最基础的行为也将无法保证,应用程序也将会变得一片混乱。

这篇关于11.《深入理解Java虚拟机》类加载器与双亲委派模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

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不兼容的问题关键实现流程总结前言作为

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应