什么是ClassLoader

ClassLoader负责将 Java 字节码装载到 JVM 中。ClassLoader本身是一个抽象类,我们用它的子类的实例对象来装载类。通常我们通过类名读入一个class文件来加载一个类。

 

classloader 层次结构图

 

  java虚拟机内部一般用三个classLoader来完成类的加载工作。

Bootstrap ClassLoader (启动类加载器)

            ----由c++实现。没有parent。启动类加载器负责加载java核心库代码

ExtClassLoader  (扩展类加载器)

            ----继承自URLClassLoader,双亲是Bootstrap ClassLoader。负责从java.ext.dirs扩展目录中加载类

AppClassLoader (系统类加载器)

            ----继承自URLClassLoader,双亲是ExtClassLoader。负责从CLASSPATH路径中加载应用程序代码。

 

  除虚拟机自带的一些类装载器外,还有一些用户自定义的类装载器。

 

ClassLoader装载的委托模式

  首先, 每一个自定义类装载器在创建时被指定一个“双亲”parent类装载器。如果在构造方法中未显式指定parent类装载器,系统类装载器就默认被指定为parent类装载器。如果传递null,则意味着指定启动类装载器为parent.

  启动类装载器没有parent。

 

  那么,类装载器之间的双亲孩子关系用来做什么呢? 它们构成了一条装载工作的委托链。 装载一个类时,先由自己定义的类装载器请求其parent装载,parent再请求它自己的parent装载,直到顶级的Bootstrap ClassLoader。 若某一级的parent能装载则装载之,否则由它的“下级”自己尝试装载。

ClassLoader.java 中的loadClass方法代码如下:

protected synchronized Class<?> loadClass(String name, boolean resolve)
	throws ClassNotFoundException
    {
	// First, check if the class has already been loaded
	Class c = findLoadedClass(name);
	if (c == null) {
	    try {
		if (parent != null) {
		    c = parent.loadClass(name, false);
		} else {
		    c = findBootstrapClass0(name);
		}
	    } catch (ClassNotFoundException e) {
	        // If still not found, then invoke findClass in order
	        // to find the class.
	        c = findClass(name);
	    }
	}
	if (resolve) {
	    resolveClass(c);
	}
	return c;
    }

    由代码结合前面的层次结构图可知,装载一个类时的优先次序为:

       启动类装载器 => 扩展类装载器 => 系统类装载器 => 用户自定义的类加载器

这也以为着首先会在最可信的JAVA核心类库中查找要装载的类,依次再去扩展路径、系统类路径(CLASSPATH)中查找,防止用户不可靠的类覆盖可信的系统类,从而提高安全性。

 

ClassLoader的命名空间

    由不同的类装载器装载的类被虚拟机放入不同的命名空间。可近似理解为虚拟机为每个由类装载器装载的类安排了一个唯一标识,形如:ClassLoaderName.packageName.ClassName. 这意味着:由同一个加载器加载的不同类拥有不同的标识;由不同类加载器加载的同一个类,其标识也不同。

    虚拟机为每一个类装载器维护一张列表,列表中是已经被装载的类型的名字。 这个列表可以保证同一个类型不会被同一个装载器装载超过一次。

    这里java虚拟机对命名空间的访问做了一些限制,具体有两条:

      1. 同一命名空间内的类可直接交互(双向)。不同命名空间的类一般不可交互,除非使用反射等显式指定的交互机制或者符合条件2.

      2. 类型可以共享。如果某个类加载器A把类型C委托给双亲委托链中的另一个加载器B,且B完成了实际装载工作,那么C在从A到B的这段双亲委托链的命名空间中共享,即C可以访问这些装载器所装载的其它类(单向的)。 by the way, 这也是我们可以直接在自己代码中使用java 核心库API的原因,因为他们被Bootstrap Loader装载。

    Over~