Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Case Analysis of JVM Class Loader and Parental delegation Model

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

这篇文章主要讲解了"JVM的类加载器和双亲委派模式实例分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"JVM的类加载器和双亲委派模式实例分析"吧!

类加载器

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

对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否"相等",只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

名称加载的类说明Bootstrap ClassLoader(启动类加载器)JAVA_HOME/jre/lib无法直接访问Extension ClassLoader(拓展类加载器)JAVA_HOME/jre/lib/ext上级为Bootstrap,显示为nullApplication ClassLoader(应用程序类加载器)classpath上级为Extension自定义类加载器自定义上级为Application1、启动类加载器

可通过在控制台输入指令,使得类被启动类加器加载,它是用C++写的,看不到源码;其他类加载器是用Java写的,说白了就是一些Java类,比如扩展类加载器、应用类加载器。

//查询所有被启动类加载器加载的类URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();for (URL url : urls) { System.out.println(url);}//查询到的结果file:/C:/Program%20Files/Java/jre1.8.0_131/lib/resources.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/rt.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/sunrsasign.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/jsse.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/jce.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/charsets.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/jfr.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/classes

由上可以看出启动类加载的都是jre和jre/lib目录下的核心库,具体路径要看你的jre安装在哪里。

2、拓展类加载器

如果classpath和JAVA_HOME/jre/lib/ext 下有同名类,加载时会使用拓展类加载器加载。当应用程序类加载器发现拓展类加载器已将该同名类加载过了,则不会再次加载

URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader().getParent()).getURLs();for (URL url : urls) { System.out.println(url);}file:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/access-bridge-64.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/cldrdata.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/dnsns.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/dns_sd.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/jaccess.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/jfxrt.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/localedata.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/nashorn.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunec.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunjce_provider.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunmscapi.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/sunpkcs11.jarfile:/C:/Program%20Files/Java/jre1.8.0_131/lib/ext/zipfs.jar

这些类库具体是什么不重要,只需要知道不同的类库可能是被不同的类加载器加载的。

3、应用程序类加载器URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();for (URL url : urls) { System.out.println(url);}file:/{项目工程目录}/bin/

这是当前java工程的bin目录,也就是我们自己的Java代码编译成的class文件所在。

4、双亲委派模式

双亲委派模式,调用类加载器ClassLoader 的 loadClass 方法时,查找类的规则。

loadClass源码

protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // 首先查找该类是否已经被该类加载器加载过了 Class c = findLoadedClass(name); //如果没有被加载过 if (c == null) { long t0 = System.nanoTime(); try { //看是否被它的上级加载器加载过了 Extension的上级是Bootstarp,但它显示为null 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) { //如果还是没有找到,先让拓展类加载器调用findClass方法去找到该类,如果还是没找到,就抛出异常 //然后让应用类加载器去找classpath下找该类 long t1 = System.nanoTime(); c = findClass(name); // 记录时间 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}

有一个描述类加载器加载类过程的术语:双亲委派模型。然而这是一个很有误导性的术语,它应该叫做单亲委派模型(Parent-Delegation Model)。但是没有办法,大家都已经这样叫了。所谓双亲委派,这个亲就是指ClassLoader里的全局变量parent,也就是父加载器。

双亲委派的具体过程如下:

当一个类加载器接收到类加载任务时,先查缓存里有没有,如果没有,将任务委托给它的父加载器去执行。

父加载器也做同样的事情,一层一层往上委托,直到最顶层的启动类加载器为止。

如果启动类加载器没有找到所需加载的类,便将此加载任务退回给下一级类加载器去执行,而下一级的类加载器也做同样的事情。

如果最底层类加载器仍然没有找到所需要的class文件,则抛出异常。

所以是一条线传上再传下,并没有什么"双亲"。

为什么要双亲委派?

答:确保类的全局唯一性。

如果你自己写的一个类与核心类库中的类重名,会发现这个类可以被正常编译,但永远无法被加载运行。因为你写的这个类不会被应用类加载器加载,而是被委托到顶层,被启动类加载器在核心类库中找到了。如果没有双亲委托机制来确保类的全局唯一性,谁都可以编写一个java.lang.Object类放在classpath下,那应用程序就乱套了。

从安全的角度讲,通过双亲委托机制,Java虚拟机总是先从最可信的Java核心API查找类型,可以防止不可信的类假扮被信任的类对系统造成危害。

5、自定义类加载器

如果我们自己去实现一个类加载器,基本上就是继承ClassLoader之后重写findClass方法,且在此方法的最后调包defineClass。

5.1、使用场景

想加载非 classpath 随意路径中的类文件

通过接口来使用实现,希望解耦时,常用在框架设计

这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于 tomcat 容器

5.2、步骤

继承ClassLoader父类

要遵从双亲委派机制,重写 findClass 方法

不是重写loadClass方法,否则不会走双亲委派机制

读取类文件的字节码

调用父类的 defineClass 方法来加载类

使用者调用该类加载器的 loadClass 方法

protected Class findClass(final String name) throws ClassNotFoundException { // 1、安全检查 // 2、根据绝对路径把硬盘上class文件读入内存 byte[] raw = getBytes(name); // 3、将二进制数据转换成class对象 return defineClass(raw);}感谢各位的阅读,以上就是"JVM的类加载器和双亲委派模式实例分析"的内容了,经过本文的学习后,相信大家对JVM的类加载器和双亲委派模式实例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report