虚拟机类加载器

类加载阶段中,“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部实现,以便让应用程序决定如何去获取所需的类,这提供了极大的灵活性。实现这个动作的代码被称为“类加载器”(ClassLoader)。

类与类加载器

对于任意一个类,必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都有一个独立的类名称空间。这句话通俗地说就是:
比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,这两个类就必定不相等。
这里所指的相等,包括代表类的Class对象的equals()方法,isAssignableFrom()方法,isInstance()、instanceof关键字的返回结果等。

各种类加载器

从Java虚拟机的角度看,只存在两种不同的类加载器:

  • 启动类加载器(BootStrap ClassLoader),C++语言实现,是虚拟机自身的一部分。
  • 其他类加载器,由Java语言实现,独立于虚拟机,继承自Java.lang.ClassLoader。

从Java开发人员的角度看,有三种类加载器:

  • 启动类加载器(BootStrap ClassLoader):这个类加载器负责加载存放在<JAVA_HOME>\lib目录,或者被-Xbootsclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的(按照文件名,如rt.jar,tools.jar)类库加载到虚拟机内存中。
    启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器处理,那直接使用null代替即可。
    JDK1.8下,使用sun.misc.Launcher.getBootstrapClassPath().getURLs()获取到所有的加载路径如下:
    BootStrapClassLoaderPath.png
    启动类加载器加载Java程序的核心基础类库,保证Java程序的正常运行。
  • 扩展类加载器(Extension Class Loader):Java代码编写,负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定路径中所有的类库。
    使用System.getProperty("java.ext.dirs")可获取扩展类加载路径。
    extClassLoader.png
    扩展类加载器是Java系统类库的一种扩展机制
  • 应用程序类加载器(Application ClassLoader),也叫系统类加载器,使用ClassLoader.getSystemClassLoader()可以获取到这个类加载器。
    负责加载用户类路径(ClassPath)上所有的类库,应用程序中默认使用的类加载器

双亲委派模型

双亲委托机制是指多个类加载器之间存在父子关系的时候,某个class类具体由哪个加载器进行加载的问题。
双亲委派模型
其具体的过程表现为:当一个类加载的过程中,它首先不会去加载,而是委托给自己的父类去加载,父类又委托给自己的父类。因此所有的类加载都会委托给顶层的父类,即Bootstrap Classloader进行加载,然后父类自己无法完成这个加载请求,子加载器才会尝试自己去加载。使用双亲委派模型,Java类随着它的加载器一起具备了一种带有优先级的层次关系,通过这种层次模型,可以避免类的重复加载,也可以避免核心类被不同的类加载器加载到内存中造成冲突和混乱,从而保证了Java核心库的安全

Launcher是JRE中用于启动程序入口main()的类,让我们看下Launcher的代码

public Launcher() {
	// Create the extension class loader
	ClassLoader extcl;
	try {
		extcl = ExtClassLoader.getExtClassLoader();
	} catch (IOException e) {
		throw new InternalError(
			"Could not create extension class loader", e);
	}

	// Now create the class loader to use to launch the application
	try {
		loader = AppClassLoader.getAppClassLoader(extcl);
	} catch (IOException e) {
		throw new InternalError(
			"Could not create application class loader", e);
	}

	// Also set the context class loader for the primordial thread.
	Thread.currentThread().setContextClassLoader(loader);
	...

为什么需要自定义类加载器?

通过灵活定义classloader的加载机制,我们可以完成很多事情,例如解决类冲突问题,实现热加载以及热部署,甚至可以实现jar包的加密保护。

更多内容可以查看

以上参考《深入理解Java虚拟机第三版》
Oracle官方Java虚拟机规范

Q.E.D.


一切很好,不缺烦恼。