Java内存管理-掌握虚拟机类加载器(五)「建议收藏」

勿在流沙筑高台,出来混迟早要还的。做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!上一篇介绍虚拟机类加载机制,讲解了类加载机制中的三个阶段,分别是:加载、连接(验证、准备、解析)、初始化 ,知道了类加载的机制。下面我们就要知道类到底是通过什么方式加载到内存中的,也就是本文要介绍的类加载器,类加载器就是加载类的信息到内存中。本文地图 : 一、什么是类加载器(…

大家好,又见面了,我是全栈君。

勿在流沙筑高台,出来混迟早要还的。

做一个积极的人

编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开!

上一篇介绍虚拟机类加载机制,讲解了类加载机制中的三个阶段,分别是:加载、连接(验证、准备、解析)、初始化 ,知道了类加载的机制。下面我们就要知道类到底是通过什么方式加载到内存中的,也就是本文要介绍的类加载器,类加载器就是加载类的信息到内存中

本文地图 :

思维地图

一、什么是类加载器(ClassLoader)

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

说简单一点:类加载器可以把类加载到Java虚拟机中,我们可以使用Java虚拟机自带的类加载器,也可以自定义实现自己的类加载器(自定义类加载器会在后面文章进行讲解)。

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用远不限于类加载阶段。对于任意的一个类,都需要由加载它的类加载器和这个类本身一同确立起在Java虚拟机中的唯一性每个类加载器都拥有一个独立的类命名空间(命名空间下面单独会介绍)。

说简单一点:比较两个类是否”相等“,只有在这两个类是由同一个类加载器加载的前提在才有意义,否则,即使这两个类源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类必定不相等。这里所指的“相等”包括代表类的Class对象的equal方法、isAssignableFrom()、isInstance()方法及instance关键字返回的结果。

两个类是否"相等"

如上图,此时虽然是同一个Person.class ,但是被不同的类加载器加载到Java虚拟机,那么加载后的这两个 Person类是不”相等“,因为它们分别在两个不同的命名空间中。

注:如果上面两个类的是否”相等“比较你没看懂的话,后面自定义类加载器的时候我们会演示一下这个例子,进行简单说明。

二、类加载器分类

一张图看懂类加载器分类:

类加载器分类

上图的类加载器主要分为四类:

  • Bootstrap ClassLoader : 启动类加载器
  • Extension ClassLoader : 扩展类加载器
  • Application ClassLoader :应用程序类加载器
  • User ClassLoader :自定义类加载器

从虚拟机角度分析,类加载器分为两类: 一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种是其他类加载器,这些类加载器是Java语言实现的,独立于虚拟机之外,都继承自抽象类java.lang.ClassLoader。

public class ClassLoaderDemo { 
   

	public static void main(String[] args) { 
   

		// ClassLoaderDemo 的类加载器
		System.out.println(ClassLoaderDemo.class.getClassLoader());

		// 打印每个 ClassLoader的父加载器
		ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
		while (classLoader != null){ 
   
			System.out.println(classLoader);
			classLoader = classLoader.getParent();
		}
		System.out.println(classLoader);
	}
}
--- 打印结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null

启动类加载器(根加载器)

主要负责加载存放在JAVA_HOME/jre/lib/rt.jar里面所有的class文件,或者被-Xbootclasspath参数所指定路径中以rt.jar命名的文件。启动类加载器无法被Java程序直接引用,如果在编写自定义类加载时,需要把加载的请求委派给启动类加载器,那么直接使用null代替即可。

例如:

MyClassLoader myClassLoader= new MyClassLoader(null); //父加载器使用启动类加载器
public MyClassLoader(ClassLoader parent){ 
   
    super(parent);//指定该类加载器的父类加载器
}

扩展类加载器

这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载AVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库。

应用程序类加载器

这个加载器由sun.misc.Launcher$AppClassLoader实现,它负责加载classpath对应的jar及目录。如果应用程序没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

自定义类加载器

用户定制自己的类加载器,继承ClassLoader,根据自己的需要进行设计和实现,比如需要从指定的路径下读取class文件进行加载等。

知道上面这些内容,你就应该联想到为什么在学习Java的时候首先要配置Java环境变量了。这就是知其所以然的过程。

思考:这么多类加载器,那么如何保证一个类不被重复加载,只被加载一次呢?

从JDK1.2版本开始,类加载过程中采用了双亲委派模型(父亲委派机制),这种机制能更好的保证Java平台的安全,在此委托机制中,除了Java虚拟机自带的根类加载(根加载器最顶级,无父加载器)以外,其余的类加载器都有且只有一个父加载器。双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有父类加载器反馈自己无法完成这个加载请求(它搜索的范围中没有找打所需的类)时,子加载器才会尝试自己去加载。

如下示意图:

双亲委派模型

在说明一下: 加载一个类的时候。首先自定义类加载类器先会委派给应用程序类加载器(Application ClassLoader),应用程序类加载器会委派给扩展类加载器(Extension ClassLoader),扩展类加载器会委派给启动类加载器(Bootstrap ClassLoader)去加载,这时候如果启动类加载器加载成功,则加载结束。如果加载失败,则交给扩展类加载器去加载,如果扩展类加载器加载成功,则加载结束。如果加载失败,则交给应用程序类加载器,如果应用程序类加载器加载成功,则加载结束。如果加载失败,则交给自定义类加载器。如果自定义类加载器加载成功,则加载结束。否则加载失败,会报ClassNotFoundExecption异常,结束。

每个类加载器都有自己的管辖范围(命名空间),并在自己的管辖范围做好自己的事情

双亲委派模型还有一个优点是能提供软件系统的安全性,在这个机制下,用户自定义的类加载器不可能加载应该由父类加载器加载的可靠类,从而防止不可靠甚至是恶意的代码代替父类加载器加载可靠代码。如 java.land.Object 类总是由启动类加载器进行加载,其他任何用户自定义的类加载器都不可能加载含有恶意代码的java.land.Object 类。(每一个看似简单的设计背后,都是大师们智慧的结晶


tips: 类加载器命名空间

每个类加载器都有自己的命名空间,命名空间由该加载器及所有父类加载器所加载的类组成。在同一个命名空间中,不会出现两个全类名(包名+类名)完全一样的类;在不同的命名空间中,有可能出现全类名相同的两个类。

类加载器命名空间

一定要注意是:有可能出现全类名一样的,就如 Loader1 中自定义类加载器可以加载如 com.aflyun.HelloJVM.java , Loader2 中自定义类加载器也可以加载如 com.aflyun.HelloJVM.java 的类,两个互不影响,并且比较两个类的话,是不”相等“的。并且不同命名空间中类加载器加载的类,不能访问其他类加载器包中的包可见(即默认访问级别)成员。

三、总结

本文主要介绍了什么是类加载器以及类加载器的分类 ,让大家对类加载器相关的知识有一个整体的认识,这样我们也知道了哪些类加载器加载什么地方的数据。为我们后面对类加载器源码和分析以及实现自定义类加载器做好铺垫。预告:下一篇文章讲一下类加载器的源码分析和设计模式,以及实现一个自定义类加载器!

四、参考资料

《深入理解Java虚拟机》

推荐阅读

Java的线程安全、单例模式、JVM内存结构等知识梳理
Java内存管理-程序运行过程(一)
Java内存管理-初始JVM和JVM启动流程(二)
Java内存管理-JVM内存模型以及JDK7和JDK内存模型对比总结(三)


谢谢你的阅读,如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到!祝你每天开心愉快!


不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页 : http://blog.csdn.net/u010648555

愿你我在人生的路上能都变成最好的自己,能够成为一个独挡一面的人

© 每天都在变得更好的阿飞云

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/121032.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • css绝对定位的参照物是什么_css 清除上定位

    css绝对定位的参照物是什么_css 清除上定位css绝对定位的重新认知所谓的css绝对定位,就是position:absolute;这里记录一个我的错误认知,就是绝对定位的参照物是内容,还是内容+内边距,我一直以为参照物就是内容,但是实际上参照物是内容+内边距看看下面的事例<!DOCTYPEhtml><html><head><metachars…

    2022年10月25日
  • php 字符串转换时间_php 字符时间如何转换「建议收藏」

    php 字符串转换时间_php 字符时间如何转换「建议收藏」php字符时间转换的方法:1、通过php中的“strtotime()”函数将任何英文文本的日期时间描述解析为时间戳;2、使用php中的“mktime()”函数从日期取得时间戳即可。本文操作环境:windows7系统、PHP5.6版,DELLG3电脑。php字符串转时间戳PHP提供了函数可以方便的将各种形式的日期转换为时间戳,该类函数主要是:strtotime():将任何英文文本的日期时间描述解…

  • 色拉英语第3集第1幕: you flatter me

    色拉英语第3集第1幕: you flatter me

  • 测试知识图谱[通俗易懂]

    测试知识图谱[通俗易懂]之前整理了一份测试知识图谱,还有一些不足,希望大家指出来。参考了数位大师的成果,下面是他人整理的。

  • 编码解码乱码字符集[通俗易懂]

    编码解码乱码字符集[通俗易懂]编码EnCode:将字符转化为字节解码DeCode:将字节转化为字符乱码:打个比方—比较多人知道的联通用字符集GBK的时候出现这样的情况:��ͨ,这就是乱码常见的中文字符集有:GBK和UTF-8国际上常用的字符集:Unicode(大字典)、ANSI UTF-8,中文占三个字节,英文占一个字节(变长),还有定长占两个字节(耗费空间多)GBK,中文占两个zijie…

  • 总结进制转换的方法_八进制计算方法

    总结进制转换的方法_八进制计算方法今天朋友去面试,面试官问他八进制转换二进制方式原理,他突然间蒙逼了,在常见的计算机知识当中,这种最基础的知识,常常会被人给忘记,所以今天我来写一篇进制转换的总结,来复习巩固一下。本文参考百度经验(https://jingyan.baidu.com/article/495ba84109665338b30ede98.html),如有侵权,请通知删除先讲一下定义吧,进制也就是进制位,对于接触过电脑的…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号