JVM内部,第1部分-类加载器

本文的翻译是专门为Java Developer课程的学生准备的。




在本系列文章中,我将讨论Java虚拟机的工作方式。 今天,我们来看一下在JVM中加载类的机制。

Java虚拟机是Java技术生态系统的核心。 Java程序可以实现“编写一次到处运行”的原则。 与其他虚拟机一样,JVM是抽象计算机。 JVM的主要任务是加载类文件并执行其中包含的字节码

JVM包括各种组件,例如类加载器垃圾收集器 (自动内存管理),解释器, JIT编译器和流控制组件。 在本文中,我们将研究类加载器。

类加载器为您的应用程序和Java API加载类文件。 仅将运行程序时真正需要的Java API类文件加载到虚拟机中。

字节码由执行引擎执行。



什么是类加载?


类加载是在程序执行期间动态搜索和加载类型(类和接口)。 类型数据在二进制类文件中。

类加载步骤


类加载器子系统不仅负责查找和导入二进制类数据。 它还对导入的类执行验证,为类变量分配和初始化内存,并帮助解析符号链接。 这些操作按以下顺序执行:

  • 加载 -按名称搜索和导入类型的二进制数据,然后根据此二进制表示形式创建类或接口。
  • 链接(链接) -验证,准备和许可(可选):
    • 验证 -验证导入类型的正确性。
    • 准备工作 —为静态类变量分配内存,并使用默认值初始化内存。
    • 解决方案 -将符号类型链接转换为直接链接。
  • 初始化是对Java代码的调用,该代码使用正确的初始值初始化类变量。

注意-除了加载类,类加载器还负责查找资源。 资源是某些数据(例如,“。class”文件,配置数据,图像),这些数据使用由“ /”字符分隔的抽象路径来标识。 资源通常与应用程序或库打包在一起,以便可以在应用程序或库代码中使用它们。

Java中的类加载机制


译者注-本节描述了Java <9的行为,在Java 9+中进行了一些小的更改,下面对此进行了描述。

Java使用类加载委托模型。 基本思想是每个类加载器都有一个“父”加载器。 加载类时 ,加载器在单独搜索该类之前将其搜索“委派”给其父级。

类加载器委托模型是将加载请求彼此传递的加载器图。 该图的根是引导引导程序。 类加载器是由一个父级创建的,可以将它们委托给其父级,并在以下位置搜索类:

  • 快取
  • 父母
  • 引导加载程序本身

类加载器首先检查是否之前已经加载了该类。 如果是这样,则返回上次返回的相同类(存储在缓存中的类)。 如果没有,父级将有机会加载该类。 递归地重复这两个步骤。 如果父级返回null(或引发ClassNotFoundException ),则加载器将自行搜索该类。

该类由最接近根的加载器加载,因为首次加载该类的权利始终授予父加载器。 这使加载程序只能查看其父级或祖先独立加载的类。 它看不到子加载器加载的类。

Java SE Platform API历史上定义了两个类加载器:

Bootstrap类加载器(基本,主加载器) -从引导类路径加载类。

系统类加载器(加载器) -新类加载的父类,通常是用于加载和运行应用程序的类加载器。

JDK 9+类加载器


应用程序类加载器 -通常用于从类路径加载应用程序类。 对于某些包含实用程序或导出实用程序API的JDK模块,它也是默认的引导加载程序。 ( 译者注:例如jdk.jconsolejdk.jshell等。

平台类加载器 -加载选定的(基于安全性/权限)Java SE和JDK模块。 例如,java.sql。

Bootstrap类加载器 -加载核心Java SE和JDK模块。

这三个内置类加载器可以按以下方式协同工作:

  • 应用程序类加载器首先查找为所有内置加载器定义的命名模块。 如果为这些加载程序之一定义了合适的模块,则此加载程序将加载该类。 如果在为这些加载程序之一定义的命名模块中找不到该类,则应用程序类加载程序会将其委托给父级。 如果父类未找到该类,则应用程序类加载器会在类路径中查找它。 在类路径中找到的类将作为此加载器的未命名模块的成员加载。
  • 平台类加载器搜索为所有内置加载器定义的命名模块。 如果为这些加载程序之一定义了合适的模块,则此加载程序将加载该类。 如果在为这些加载程序之一定义的命名模块中找不到该类,则平台类加载程序将其委托给父级。
  • 引导类加载器搜索为其定义的命名模块。 如果在为引导程序引导程序定义的命名模块中找不到该类,则引导程序引导程序使用-Xbootclasspath /参数搜索添加到引导程序路径的文件和目录(允许您将文件和目录添加到引导程序路径)。 在引导程序类路径中找到的类将作为此加载器的未命名模块的成员加载。

要查看内置的类加载器,可以使用以下代码:

 package ru.deft.homework; import java.sql.Date; public class BuiltInClassLoadersDemo { public static void main(String[] args) { BuiltInClassLoadersDemo demoObject = new BuiltInClassLoadersDemo(); ClassLoader applicationClassLoader = demoObject.getClass().getClassLoader(); printClassLoaderDetails(applicationClassLoader); // java.sql classes are loaded by platform classloader java.sql.Date now = new Date(System.currentTimeMillis()); ClassLoader platformClassLoder = now.getClass().getClassLoader(); printClassLoaderDetails(platformClassLoder); // java.lang classes are loaded by bootstrap classloader ClassLoader bootstrapClassLoder = args.getClass().getClassLoader(); printClassLoaderDetails(bootstrapClassLoder); } private static void printClassLoaderDetails(ClassLoader classLoader) { // bootstrap classloader is represented by null in JVM if (classLoader != null) { System.out.println("ClassLoader name : " + classLoader.getName()); System.out.println("ClassLoader class : " + classLoader.getClass().getName()); } else { System.out.println("Bootstrap classloader"); } } } 

通过在我安装的Amazon Corretto 11.0.3上运行此代码,我们得到以下结果:

 ClassLoader name : app ClassLoader class : jdk.internal.loader.ClassLoaders$AppClassLoader ClassLoader name : platform ClassLoader class : jdk.internal.loader.ClassLoaders$PlatformClassLoader Bootstrap classloader 

您可以在此处(JDK 11)了解更多有关ClassLoader API的信息

Source: https://habr.com/ru/post/zh-CN468193/


All Articles