Java类加载器有几种?什么是双亲委派模型?

什么是类加载器

类加载器就是加载磁盘中的字节码文件(.class)的到内存中的类;
类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中

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

类加载器可以说是Java语言的一项创新,也是Java语言流行的重要原因之一,它最初是为了满足Java Applet的需求而开发出来的。虽然目前Java Applet技术基本上已经“死掉”,但类加载器却在类层次划分、OSGi、热部署、代码加密等领域大放异彩,成为了Java技术体系中一块重要的基石,可谓是失之桑榆,收之东隅。
——《深入理解Java虚拟机》

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
——《深入理解Java虚拟机》

类加载器种类

BootstrapClassLoader

启动类加载器/根类加载器
负责将 <Java_Home>/jre/lib下面的类库加载到内存中(比如/jre/lib/rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。不继承自java.lang.ClassLoader
也加载-Xbootclasspath参数指定路径,但是必须是虚拟机识别的(仅按照文件名识别),否则就算放在/lib目录下,也不会被根类加载器加载。
根类加载器使用C++实现(HotSpot),是虚拟机的一部分。是唯一一个使用本地代码编写的加载器。
之前看到一个网友提问:“既然所有的Java类,都是由类加载器加载的,那么BootstrapClassLoader,首先是一个类,它是由谁来加载的呢?”。
其实BootstrapClassLoader,不是一个java类。我们在IDE工具中,也引用不了该类。它是在JVM启动时运行的一个特殊的系统类加载器。

由于BootstrapClassLoader对Java不可见,所以返回了null,我们也可以通过某一个类的加载器是否为null来作为判断该类是不是使用BootstrapClassLoader进行加载的依据

在这里插入图片描述
图:BootstrapClassLoader不是一个java类

-Xbootclasspath参数

参见:oracle -X Command-line Options

-Xbootclasspath
This option specifies a semicolon-separated list of directories, JAR archives, and ZIP archives to search for boot class files. These are used in place of the boot class files included in the Java 2 SDK.

Note: Applications that use this option to override a class in rt.jar should not be deployed. Doing so would contravene the Java 2 Runtime Environment binary code license.
Operation
Format: -Xbootclasspath <directories and zips/jars separated by ; (Windows) or : (Linux)>

Enter this option at startup to create the default classpath for bootstrap classes and resources. This option must be entered in lower case, not camel notation, as shown in the example.

通过阅读oracle的官方文档,我们得知:-Xbootclasspath指令用于指定用来代替java2 SDK中包含的引导类文件的、已分号分隔(windows ; linux :)的目录。不能重写rt.jar中的类。而且这个选项必须输入全小写字母,而不是驼峰格式的词汇。

jdk8中,系统定义的包有:

charsets.jar
deploy.jar
javaws.jar
jce.jar
jfr.jar
jfxswt.jar
jsse.jar
management-agent.jar
plugin.jar
resources.jar
rt.jar

ExtClassLoader

扩展类加载器
继承自java.lang.ClassLoader
加载<JAVA_HOME>/jre/lib/ext目录下的类,或者被java.ext.dirs系统变量指定的路径中的所有类库。它的父加载器是null(因为BootstrapClassLoader是使用C++实现的,没有对应的java类)

jdk8中,系统定义的包有:

cldrdata.jar
dnsns.jar
jaccess.jar
jfxrt.jar
localedata.jar
nashorn.jar
sunec.jar
sunjce_provider.jar
sunpkcs11.jar
zipfs.jar

AppClassLoader

应用程序类加载器,又称系统类加载器。
是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统(System)加载器。

双亲委派模型

  • 意义:避免重复加载,避免不安全因素(如果不采用这种机制,那么系统核心的类就可以被随意替换)

概念

即一个类加载器在加载类时,先把这个请求委托给自己的父类加载器去执行,
如果父类加载器还存在父类加载器,就继续向上委托,直到顶层的启动类加载器。
如果父类加载器能够完成类加载,就成功返回,
如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载

在这里插入图片描述
图:双亲委派模型

附加源码

/**
 * <pre>
 * 程序目的:1. 分析不同的Java类(包括接口),是由哪一种类加载器进行加载的。
 * 2. 验证 BootstrapClassLoader 启动类/根类加载器,加载的目录是"<JAVA_HOME>/lib"还是"<JAVA_HOME>/jre/lib"
 *
 * 验证步骤:
 * 1. 获取<JAVA_HOME>/jre/lib下的类,打印查看该类是由哪一种类加载器加载的
 * 2. 获取<JAVA_HOME>/lib下的类,打印查看该类是由哪一种类加载器加载的
 * </pre>
 * created at 2020-06-09 07:00
 * @author lerry
 */
public class ClassLoaderDemo {
	public static void main(String[] args) {
		// BootstrapClassLoader
		System.out.println("查看<JAVA_HOME>/jre/lib 下的类,使用的是哪种类加载器");
		System.out.printf("/jre/lib/charsets.jar 中的 X11GB2312 的类加载器是:%s\n", X11GB2312.class.getClassLoader());
		System.out.printf("/jre/lib/rt.jar 中的 Object 的类加载器是:%s\n", Object.class.getClassLoader());
		System.out.printf("/jre/lib/rt.jar 中的 BigDecimal 的类加载器是:%s\n", BigDecimal.class.getClassLoader());

		System.out.println("------------------------------------------------------");
		// ExtClassLoader
		System.out.println("查看<JAVA_HOME>/jre/lib/ext 下的类,使用的是哪种类加载器");
		ClassLoader extClassLoader = FormatData_aa.class.getClassLoader();
		System.out.printf("/jre/lib/ext/cldrdata.jar 中的 FormatData_aa 的类加载器是:%s\n", extClassLoader);
		System.out.println("------------------------------------------------------");
		// AppClassLoader
		System.out.println("查看开发者自定义的类,使用的是哪种类加载器");
		ClassLoader appClassLoader = ClassLoaderDemo.class.getClassLoader();
		System.out.printf("自己写的类使用的类加载器是:%s\n", appClassLoader);


		System.out.println("------------------------------------------------------");
		// 演示双亲委派模式
		while (appClassLoader != null) {
			System.out.printf("目前还不是最顶级的类加载器:%s,继续执行getParent()方法获取父类加载器\n", appClassLoader);
			appClassLoader = appClassLoader.getParent();
		}
		System.out.println("------------------------------------------------------");
		System.out.println("查看<JAVA_HOME>/lib下的类,使用的是哪种类加载器");
		System.out.printf("/lib/tools.jar 中的 Datatype 的类加载器是:%s\n", Datatype.class.getClassLoader());
		System.out.printf("/lib/ant-javafx.jar 中的 LauncherUserJvmOptions 的类加载器是:%s\n", LauncherUserJvmOptions.class.getClassLoader());
	}
}

在这里插入图片描述
图:<JAVA_HOME>/lib包tools.jar
在这里插入图片描述
图:<JAVA_HOME>/lib包ant-javafx.jar
在这里插入图片描述
图:/lib/tools.jar中的Datatype接口
在这里插入图片描述
图:/lib/ant-javafx.jar中的LauncherUserJvmOptions类

运行结果

查看<JAVA_HOME>/jre/lib 下的类,使用的是哪种类加载器
/jre/lib/charsets.jar 中的 X11GB2312 的类加载器是:null
/jre/lib/rt.jar 中的 Object 的类加载器是:null
/jre/lib/rt.jar 中的 BigDecimal 的类加载器是:null
------------------------------------------------------
查看<JAVA_HOME>/jre/lib/ext 下的类,使用的是哪种类加载器
/jre/lib/ext/cldrdata.jar 中的 FormatData_aa 的类加载器是:sun.misc.Launcher$ExtClassLoader@548c4f57
------------------------------------------------------
查看开发者自定义的类,使用的是哪种类加载器
自己写的类使用的类加载器是:sun.misc.Launcher$AppClassLoader@18b4aac2
------------------------------------------------------
目前还不是最顶级的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2,继续执行getParent()方法获取父类加载器
目前还不是最顶级的类加载器:sun.misc.Launcher$ExtClassLoader@548c4f57,继续执行getParent()方法获取父类加载器
------------------------------------------------------
查看<JAVA_HOME>/lib下的类,使用的是哪种类加载器
/lib/tools.jar 中的 Datatype 的类加载器是:sun.misc.Launcher$AppClassLoader@18b4aac2
/lib/ant-javafx.jar 中的 LauncherUserJvmOptions 的类加载器是:sun.misc.Launcher$AppClassLoader@18b4aac2

源码运行结论

/lib/tools.jar 中的 Datatype 和 /lib/ant-javafx.jar 中的 LauncherUserJvmOptio
的类加载器是AppClassLoader,
而 /jre/lib/rt.jar 中的 BigDecimal 的类加载器是 BootstrapClassLoader。
通过验证得知,<JAVA_HOME>/jre/lib目录下的类,是由BootstrapClassLoader(启动类加载器)加载的。

启动类加载器和扩展类加载器,加载java运行时环境用到的类(<JAVA_HOME>/jre目录下)

环境说明

  • jdk version:1.8.0_144
  • OS:macOS High Sierra 10.13.4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值