编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

技术进阶:再议类加载器—从Java 9开始的类加载器内部机制的变化

wxchong 2024-08-06 03:37:06 开源技术 24 ℃ 0 评论

类加载器

前一天发布了一篇关于Java类加载器的文章,详见:

技术进阶:深入浅出Java类加载机制及类加载器关系和变化

可以说,从Java 9开始,JVM的类加载模式和机制,在内部做了很大改变。容我慢慢从头道来。

在运行时,每个类型都由类加载器加载,类加载器由java.lang.ClassLoader类的实例表示。 您可以使用Class类的getClassLoader()方法获取某一类型的类加载器的引用。 以下代码片段显示了如何获取Bulb类(假设为有效类)的类加载器:

Class<Bulb> cls = Bulb.class;

ClassLoader loader = cls.getClassLoader();

类加载器在JDK9中有所改变。 但是,类加载和类加载器的代码行为在JDK9中保持不变。 在以下部分,为阅读完整起见,我将再次描述JDK8和JDK9中的类加载器。

JDK8中类加载器

在JDK9之前,运行时使用三个类加载器来加载类,如下图所示。 箭头的方向表示委派方向。 这些类加载器从不同位置和不同类型加载类。 您可以添加更多类加载器,它们将是ClassLoader类的子类。使用自定义类加载器,您可以从自定义位置加载类、隔离用户代码及卸载类。对于简单的应用程序,内置类加载器就足够了。

类加载器以分层方式工作——引导类加载器位于层次结构的顶部。 类加载器将类的加载请求委托给其上面的类加载器。 例如,如果请求应用程序类加载器加载类,它会将请求委托给扩展类加载器,扩展类加载器又将请求委托给引导类加载器。 如果引导类加载器无法加载类,则扩展类加载器会尝试加载它。 如果扩展类加载器无法加载类,则应用程序类加载器会尝试加载它。 如果应用程序类加载器无法加载它,则抛出ClassNotFoundException。

引导(Bootstrap)类加载器是扩展类加载器的父级。 扩展(Extension)类加载器是应用程序(Application)类加载器的父级。 引导类加载器没有父级。 默认情况下,应用程序类加载器将是您创建的其他类加载器的父级。

注意:可以使用ClassLoader类的getParent()方法获取类加载器父级的引用

引导类加载器加载构成Java平台的引导类,包括JAVA_HOME\lib\rt.jar中的类和其他几个运行时JAR。 它完全在虚拟机中实现。 您可以使用-Xbootclasspath/p和-Xbootclasspath/a命令行选项来预先添加和追加其他引导程序目录。 您可以使用-Xbootclasspath选项指定引导类路径,该选项将替换默认的引导类路径。 在运行时,sun.boot.class.path系统属性包含引导类路径的只读值。 引导类加载器由null表示。 也就是说,你无法得到它的参考。 例如,Object类由引导类加载器加载,表达式Object.class.getClassLoader()返回null。

通过扩展机制,扩展类加载器用于加载由java.ext.dirs系统属性指定的目录中的JAR中可用的类。 要获取扩展类加载器的引用,您需要获取应用程序类加载器的引用(请参阅下一段)并在该引用上使用getParent()方法。

应用程序类加载器从CLASSPATH环境变量或命令行选项-cp或-classpath指定的应用程序类路径加载类。应用程序类加载器也称为系统类加载器,它是一种误称,它给人一种错误的印象,即加载系统类。 您可以使用ClassLoader类的名为getSystemClassLoader()的静态方法获取应用程序类加载器的引用。

JDK9中类加载器

JDK9保留了三级分层类加载器体系结构,以实现向后兼容。 但是,从模块系统加载类的方式有一些变化。 下图显示了JDK9类加载器层次结构。

请注意,在JDK9中,应用程序类加载器可以委托给平台类加载器以及引导类加载器; 平台类加载器可以委托给应用程序类加载器。

在JDK9中,引导类加载器在库代码和虚拟机中实现。为了向后兼容,它在程序中仍然由null表示。 例如,Object.class.getClassLoader()仍然返回null。 并非所有Java SE平台和JDK模块都由引导类加载器加载。 仅举几个例子,bootstrap类加载器加载的模块是java.base,java.logging,java.prefs和java.desktop。 其他Java SE Platform和JDK模块由平台类加载器和应用程序类加载器加载,下面将对其进行描述。JDK9不再支持指定引导类路径,-Xbootclasspath和-Xbootclasspath/p以及系统属性sun.boot.class.path的选项。仍支持-Xbootclasspath/a选项,其值存储在系统属性jdk.boot.class.path.append中。

JDK9+不再支持扩展机制。 但是,它在名为平台类加载器(platform class loader)的新名称下保留了扩展类加载器。 ClassLoader类包含一个名为getPlatformClassLoader()的新静态方法,它返回平台类加载器的引用。 下表列出了平台类加载器加载的模块。

平台类加载器用于其他目的。 默认情况下,引导类加载器加载的类被授予所有权限。 但是,有几个类不需要所有权限。 这些类在JDK9中已取消特权,它们由平台类加载器加载。

应用程序类加载器加载模块路径上的应用程序模块和一些提供工具或导出工具API的JDK模块,如表3-4中所列。 在JDK9中,您仍然可以使用ClassLoader类的名为getSystemClassLoader()的静态方法来获取应用程序类加载器的引用。

下表由JDK9中的应用类加载器(Application Class Loader)加载的JDK模块

注意:在JDK9之前,扩展类加载器和应用程序类加载器是java.net.URLClassLoader类的实例。 在JDK9+中,平台类加载器(可看作以前的扩展类加载器)和应用程序类加载器是内部JDK类的实例。 如果您的代码依赖于特定于URLClassLoader类的方法,那么您的代码可能会在JDK9中中断。

前两表中未列出的JDK模块由引导类加载器加载。 清单3-4显示了如何打印模块名称及其类加载器名称。 显示了部分输出。 输出取决于运行时解析的模块。 要打印所有JDK模块及其类加载器,您应该在运行此类之前在模块声明中添加"requires java.se.ee"。 我适时将在后续篇章讨论模块层。

示例

按类加载器列出已加载模块的名称

public class ModulesByClassLoader {
 public static void main(String[] args) {
 // 获取引导层
 ModuleLayer layer = ModuleLayer.boot();
 //打印模块名和类加载器名
 for (Module m : layer.modules()) {
 ClassLoader loader = m.getClassLoader();
 String moduleName = m.getName();
 String loaderName = loader == null ? "bootstrap" : loader.getName();
 System.out.printf("%s: %s%n", loaderName, moduleName);
 }
 }
}

参考结果示意:

platform: java.xml.ws

app: jdk.compiler

platform: java.transaction

platform: jdk.naming.dns

bootstrap: java.datatransfer

bootstrap: jdk.jfr

app: jdk.jlink

...

JDK9+中的类加载机制发生了一些变化。 三个内置类加载器协同工作以加载类。 当应用程序类加载器需要加载类时,它会搜索定义给所有类加载器的模块。 如果为这些类加载器之一定义了合适的模块,那么该类加载器将加载该类,这意味着应用程序类加载器现在可以委托给引导类加载器和平台类加载器。 如果在为这些类加载器定义的命名模块中找不到类,则应用程序类加载器将委托给其父级,即平台类加载器。 如果仍未加载类,则应用程序类加载器将搜索类路径。 如果它在类路径上找到类,则会将该类作为其未命名模块的成员加载。 如果它在类路径上找不到类,则抛出ClassNotFoundException。

当平台类加载器需要加载类时,它会搜索定义给所有类加载器的模块。 如果为这些类加载器之一定义了合适的模块,那么该类加载器将加载该类,这意味着平台类加载器可以委托给引导类加载器以及应用程序类加载器。 如果在为这些类加载器定义的命名模块中找不到类,则平台类加载器将委托给其父级,即引导类加载器。

当引导类加载器需要加载类时,它会搜索自己的命名模块列表。 如果找不到类,它将搜索通过命令行选项-Xbootclasspath/a指定的文件和目录列表。 如果它在引导类路径上找到一个类,它会将该类作为其未命名模块的成员加载。 如果仍未找到类,则抛出ClassNotFoundException。

P.S:这里的很多内容,要很好的理解,需要有Java模块的知识背景。关于这块内容,本号也会适时推出,敬请关注。

好了,就写这些了,希望对你有帮助,顺手点个赞,再分享出去吧^_^

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表