ClassLoader工作机制

1. ClassLoader概念

简单理解:ClassLoader就是类加载器,用来动态加载class文件到JVM中,并转换成java.lang.class类的一个实例。与普通程序不同的是。Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里面运行,负责加载Java class的这部分就叫做ClassLoader。
在这里插入图片描述

2. Java默认提供的ClassLoader

Java默认提供三个ClassLoader, BootStrap ClassLoader、
Extension ClassLoader和App ClassLoader。
在这里插入图片描述

3. ClassLoader之间的关系

ExtClassLoader和AppClassLoader都继承了URLClassLoader类,而URLClassLoader又实现了抽象类ClassLoader,在创建Launcher对象的时候首先会创建ExtClassLoader,然后将ExtClassLoader对象作为父加载器创建AppClassLoader对象,而通过Launcher.getClassLoader()方法获取的ClassLoader就是AppClassLoader对象。所以如果在java应用中没有定义其它ClassLoader,那么除了System.getProperty(“java.ext.dirs”)目录下的类是由ExtClassLoader加载外,其它类都由AppClassLoader来加载。
在这里插入图片描述
Launcher源代码:

public class Launcher {

	public Launcher() { 
		try {
			 extcl = ExtClassLoader.getExtClassLoader();// 创建ExtClassLoader
		 } catch (IOException e) { 
			 throw new InternalError( "Could not create extension classloader", e);
		 } 
		try { 
			 loader = AppClassLoader.getAppClassLoader(extcl); // 将ExtClassLoader对象作为父加载器创建AppClassLoader

		 } catch (IOException e) {
			 throw new InternalError( "Could not create application classloader", e); 
		 }
	}
	
	……
	
	static class ExtClassLoader extends URLClassLoader {};// ExtClassLoader继承URLClassLoader类
	
	……
	
	static class AppClassLoader extends URLClassLoader {};// AppClassLoader继承URLClassLoader类
	
	……

从如上的代码我们可以看出,Launcher 类中包含 ExtClassLoader 和 AppClassLoader,而ExtClassLoader 和 AppClassLoader又继承自 URLClassLoader。在 Launcher的构造方法中, 会先通过ExtClassLoader.getExtClassLoader()方法创建 ExtClassLoader 的对象 extcl,然后再将extcl作为父加载器创建AppClassLoader的对象。

4. 每个类加载器都有一个父加载器

在这里插入图片描述

4.1 ClassLoader的父加载器

首先,我们要明确的知道一点,父加载器不是父类,一个类的父加载器有两种情况:

  1. 由外部类创建ClassLoader时直接指定一个ClassLoader为parent。
  2. 由getSystemClassLoader()方法生成,也就是在sun.misc.Laucher通过getClassLoader()获取,也就是上面我们所说过的AppClassLoader。直白的说,一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader。

ClassLoader源代码:

   public abstract class ClassLoader {

	private final ClassLoader parent;
	private static ClassLoader scl;
	private ClassLoader(Void unused, ClassLoader parent) {
 		this.parent = parent; 
	…
	} 

	protected ClassLoader(ClassLoader parent) { 
		this(checkCreateClassLoader(), parent); // 指定parent创建
	} 

	protected ClassLoader() { 
		this(checkCreateClassLoader(), getSystemClassLoader());// 未指定parent,由getSystemClassLoader()方法生成
 	}

	public final ClassLoader getParent() 
	{ 
		if (parent == null) return null; 
		return 	parent; 
	}
	…
	public static ClassLoader getSystemClassLoader() {
	initSystemClassLoader();
	if (scl == null) {
		return null;
	}
	 return scl; // 返回scl
	}

	private static synchronized void initSystemClassLoader() {
		if (!sclSet) {
			if (scl != null)
				throw new IllegalStateException("recursive invocation");
			sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
			if (l != null) { 
				Throwable oops = null;
				scl = l.getClassLoader();// scl赋值
			…		
			}
			…
		}
		…
	}

从ClassLoader的源代码我们可以看到,ClassLoader中有两个受保护的构造方法,其中一个需要传入parent参数,通过parent创建ClassLoader;另一个是无参构造方法,在这个无参构造方法中则是通过getSystemClassLoader()创建ClassLoader。在getSystemClassLoader()方法中我们可以看到返回的是一个scl的对象,而在scl的初始化话方法initSystemClassLoader()里面,scl是通过sun.misc.Launcher.getClassLoader()方法赋值的。

4.2 Bootstrap ClassLoader

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。比如ExtClassLoader。因此通过ExtClassLoader的getParent方法获取的值为Null,但它仍然是具有父加载器的。

5. 双亲委托加载方式

一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先“询问”父加载器是否加载,如果没有,则再往上询问,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托。
在这里插入图片描述
如上图所示,双亲委托加载机制有分为5个步骤:

  1. 一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。
  2. 递归,重复第1部的操作。
  3. 如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面,它首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径。找到就返回,没有找到,让子加载器自己去找。
  4. Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找。
  5. ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路径下查找。找到就返回。如果没有找到就让子类找,如果没有子类会怎么样?抛出各种异常。

LoadClass方法源码:

protected Class<?> loadClass(String name, boolean resolve)
	throws ClassNotFoundException{ 		
	synchronized (getClassLoadingLock(name)) {
		  // 检测class文件是否已经加载
	      Class<?> c = findLoadedClass(name);
			if (c == null) {// class文件未加载
				long t0 = System.nanoTime(); 
				try {
					if (parent != null) {
						// 父加载器不为空则递归调用父加载器的loadClass
						c = parent.loadClass(name, false);
					} else {
						//父加载器为空则调用Bootstrap Classloader 
						c = findBootstrapClassOrNull(name); 
					}
				} catch (ClassNotFoundException e) {
				} 
			if (c == null) {
				long t1 = System.nanoTime
				 c = findClass(name);// 父加载器没有找到,则调用自身的findclass方法查找
			…
			}
		    …
	     }
	     if (resolve) {
			resolveClass(c); // 调用resolveClass()生成最终的class实例
	     } 
	     return c;
	}
}

在这里插入图片描述
通过上面代码和图片我们可以看出,类加载具体步骤如下:

  1. 执行findLoadedClass(String)去检测这个class是不是已经加载过了。
  2. 执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器。
  3. 如果向上委托父加载器没有加载成功,则通过findClass(String)查找。
  4. 调用resolveClass(Class)这个方法来生成最终的Class对象。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值