zl程序教程

您现在的位置是:首页 >  后端

当前栏目

jvm核心类加载器--jdk源码剖析 (下)

JVMJDK源码 -- 加载 核心 剖析
2023-09-27 14:25:58 时间
二. jvm核心类加载器


 类主要通过类加载器来加载, java里面有如下几种类加载器


1. 引导类加载器:负责加载支撑JVM运行的, 位于jre目录的lib目录下的核心类. 比如:rt.jar, charset.jar等,


2. 扩展类加载器: 负责加载支撑JVM运行的, 位于jre目录的lib/ext扩展目录中的jar包


3. 应用程序类加载器: 负责加载classPath路径下的类包, 主要加载自己写的类


4. 自定义类加载器: 负责加载用户自定义路径下的类包


  引导类加载器是由C 帮我们实现的, 然后c 语言会通过一个Launcher类将扩展类加载器(ExtClassLoader)和应用程序类加载器(AppClassLoader)构造出来, 并且把他们之间的关系构建好. 


例1: 
package com.lxl.jvm;

import sun.misc.Launcher;

import java.net.URL;

public class TestJDKClassLoader {

 public static void main(String[] args) {

 * 第一个: String 是jdk自身自带的类, 所以, 他的类加载器是引导类加载器

 * 第二个: 加密类的classloader, 这是jdk扩展包的一个类

 * 第三个: 是我们当前自己定义的类, 会被应用类加载器加载

 System.out.println(String.class.getClassLoader());

 System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());

 System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());

}

我们来看这个简单的代码, 运行结果:

null

sun.misc.Launcher$ExtClassLoader

sun.misc.Launcher$AppClassLoader


第一个: String 是jdk自身自带的类, 所以, 他的类加载器是引导类加载器


 第二个: 加密类的classloader, 这是jdk扩展包的一个类, jdk扩展包里面使用的是extClassLoader类加载器加载的


 第三个: 是我们当前自己定义的类, 会被AppClassLoader应用程序加载器加载.


我们看到ExtClassLoader和AppClassLoader都是Launcher类的一部分. 那Launcher类是什么东西呢?


上面有提到, Launcher类是jvm启动的时候由C 调用启动的一个类. 

 

那么,第一个bootstrap引导类加载器, 那引导类加载器返回的为什么是null呢? 


因为bootstrap引导类加载器, 他不是java的对象, 他是c 生成的对象, 所以这里是看不到的

 

 例2: ExtClassLoader和AppClassLoader是怎么生成的?

1187916-20200628144806628-589943450.png

 从这个图中我们可以看出,C 调用java创建JVM启动器, 其中一个启动器是Launcher,他实际是调用了sun.misc.Launcher的getLauncher()方法. 那我们就从这个方法入手看看到底是如何运行的?


我们看到Lanucher.java类是在核心的rt.jar包里的

1187916-20200628144729188-756451986.png

我们看到getLauncher()类直接返回了launcher. 而launcher是一个静态对象变量, 这是一个单例模式


C 通过getLauncher()-- 直接返回了lanucher对象, 而launcher对象是在构建类的时候就已经初始化好了. 那么,初始化的时候做了哪些操作呢?接下来看看他的构造方法.

1187916-20200628150030761-405200018.png


 在构造方法里, 首先定义了一个ExtClassLoader. 这是一个扩展类加载器, 扩展类加载器调用的是getExtClassLoader(). 接下来看一看getExtClassLoader这个方法做了什么?

1187916-20200628163613077-212599620.png

 在这里, 判断当前对象是否初始化过, 如果没有, 那么就创建一个ExtClassLoader()对象, 看看createExtClassLoader()这个方法做了什么事呢?


1187916-20200628163818377-2147069863.png


doPrivileged是一个权限校验的操作, 我们可以先不用管, 直接看最后一句, return new Launcher.ExtClassLoader(var1). 直接new了一个ExtClassLoader, 其中参数是var1, 代表的是ext扩展目录下的文件.


1187916-20200628164153699-67136716.png


在ExtClassLoader(File[] var1)这个方法中,  这里第一步就是调用了父类的super构造方法. 而ExtClassLoader继承了谁呢? 我么你可以看到他继承了URLClassLoader.


1187916-20200628164328030-860213917.png

 而URLClassLoader是干什么用的呢? 其实联想一下大概能够猜数来, 这里有一些文件路径, 通过文件路径加载class类.


我们继续看调用的super(parent), 我们继续往下走, 就会看到一下内容

1187916-20200628164731067-495198192.png

 这里设置了ExtClassLoader的parent是谁? 注意看的话,我们发现, ExtClassLoader的parent类是null. 

1187916-20200628164842440-1589364872.png


 这就是传递过来的parent类加载器, 那么这里的parent类加载器为什么是null呢? 因为, ExtClassLoader的父类加载器是谁呢? 他是Bootstrap ClassLoader. 而BootStrap ClassLoader是C 的类加载器, 我们不能直接调用它, 所以, 设置为null.

 

其实, ExtClassLoader在初始化阶段就是调用了ExtClassLoader方法, 初始化了ExtClassLoader类

 

接下来,我们回到Launcher的构造方法, 看看Launcher接下来又做了什么?

1187916-20200628165433071-2026979652.png

可以看到, 接下来调了AppClassLoader的getAppClassLoader(var1), 这个方法. 需要注意一下的是var1这个参数. var1是谁呢? 向上看, 可以看到var1是ExtClassLoader.

1187916-20200628165938469-1676277803.png


这是AppClassLoader, 应用程序类加载器, 这个类是加载我们自己定义的类的类加载器. 他也是继承自URLClassLoader. 

我们来看看getAppClassLoader(final ClassLoader var0)方法. 这个方法的参数就是上面传递过来的ExtClassLoader

这里第一句话就是获取当前项目的class 文件路径, 然后将其转换为URL. 并调用了Launcher.AppClassLoader(var1x, var0), 其中var1x是class类所在的路径集合, var0是扩展的类加载器ExtClassLoader, 接下来, 我们进入到这个方法里看一看


1187916-20200628170425337-1889803398.png


AppClassLoader直接调用了其父类的构造方法, 参数是class类路径集合, 和ExtClassLoader

1187916-20200628170554270-1818581711.png

1187916-20200628170635557-567845896.png


 最后, 我们看到, 将ExtClassLoader传递给了parent变量. 这是定义在ClassLoader中的属性, 而ClassLoader类是所有类加载器的父类. 因此, 我们也可以看到AppClassLoader的父类加载器是ExtClassLoader 

 

同时, 我们也看到了, C 在启动JVM的时候, 调用了Launcher启动类, 这个启动类同时加载了ExtClassLoader和AppClassLoader.

 

public static void main(String[] args) {

 ClassLoader appClassLoader ClassLoader.getSystemClassLoader();

 ClassLoader extClassLoader appClassLoader.getParent();

 ClassLoader bootstrapClassLoad extClassLoader.getParent();


System.out.println( bootstrap class loader: bootstrapClassLoad); System.out.println( ext class loader extClassLoader); System.out.println( app class loader appClassLoader); }


通过这个demo, 我们也可以看出, appClassLoader的父类是extClassLoader, extClassLoader的父类是bootstrapClassLoader


输出结果:

bootstrap class loader: null

ext class loader sun.misc.Launcher$ExtClassLoader 2a84aee7

app class loader sun.misc.Launcher$AppClassLoader 18b4aac2 

JVM系列(二):JVM中类加载器相关知识笔记 JVM虚拟机运行的文件是class文件,它是由我们的Java程序编译后产生的文件。 类的加载:JVM虚拟机将指定的class文件读取到内存里,并解释运行该class文件里的Java程序的过程。 类的卸载:将某个class文件的运行时数据从JVM虚拟机中移除的过程。
【JVM深度解析】类加载与类加载器 你了解类加载机制吗?类加载器能说一下是什么吗?如何破坏双亲委派呢,多说几种?...不懂?一文带你了解类加载与类加载器