深入理解JVM(③)虚拟机的类加载器(双亲委派模型)

先解释一下什么是类加载器,通过一个类的全限定名来获取描述该类的二进制字节流,在虚拟机中实现这个动作的代码被称为“类加载器(Class Loader)”。

类与类加载器

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段。每个类加载器都有一个独立的类名称空间,所以每个类唯一性都必须是建立在是否为同一个类加载器的前提下的。
否则,即使是两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
例如:

public class ClassLoaderOneTest { public static void main(String[] args) throws Exception{ ClassLoader oneLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String classFileName = name.substring(name.lastIndexOf(".")+1)+".class"; InputStream inputStream = getClass().getResourceAsStream(classFileName); if(inputStream == null){ return super.loadClass(name); } byte[] bytes = new byte[inputStream.available()]; inputStream.read(bytes); return defineClass(name,bytes,0,bytes.length); }catch (IOException e){ throw new ClassNotFoundException(name); } } }; Object object = oneLoader.loadClass("com.eurekaclient2.test.jvm3.SonClass").newInstance(); System.out.println(object.getClass()); System.out.println("instanceof result :"+ (object instanceof com.eurekaclient2.test.jvm3.SonClass)); } }

运行结果:

class com.eurekaclient2.test.jvm3.SonClass instanceof result :false

通过上面的运行结果可以看出,自定义的类加载器加载出来的类创建的对象和com.eurekaclient2.test.jvm3.SonClass在做类型检查时返回了false,这是因为在Java虚拟机中存在的两个SonClass,一个是由虚拟机的应用程序类加载器所加载的,另一个是由自定义的类加载器加载的,虽然来自同一个Class文件,但在Java虚拟机中是两个互相独立的类。

双亲委派模型

Java虚拟机把类加载器分为了两大类:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器是虚拟机的一部分。另外一种就是其他类加载器(全部继承自java.lang.ClassLoader)。
从JDK1.2开始至JDK9之前的Java应用绝大多数都会使用到如下3个系统提供的类加载器进行加载。

启动类加载器(Bootstrap Class Loader):这个类加载器复制加载存放在<JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的类库加载到虚拟机的内存中。
如果需要使用引导类加载器去加载类,直接使用null代替即可。
如下是ClassLoader.getClassLoader()方法的源码:

/** * Returns the class loader for the class. Some implementations may use * null to represent the bootstrap class loader. This method will return * null in such implementations if this class was loaded by the bootstrap * class loader. * / @CallerSensitive public ClassLoader getClassLoader() { ClassLoader cl = getClassLoader0(); if (cl == null) return null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass()); } return cl; }

扩展类加载器(Extension Class Loader):这个类加载器是在类sun.misc.launcher$ExtClassLoader中以Java代码的形式实现的。它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。这是Java系统类库的扩展机制,但是在JDK9之后,被模块化能力所替代了。

应用程序类加载器(Application Class Loader):这个类加载器由sun.misc.Launcher$AppClassLoader来实现的。它负责加载用户类路径(ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如没有自定义的类加载器,这个就是默认的类加载器。

在JDK9之前的Java应用都是由这三类加载器互相配合来完成加载的,如果有自定义的类加载器,会先执行自定义的类加载器。各种的类加载器之间的层次关系被称为类加载器的“双亲委派模型(Parents Delegation Model)”。
各种类加载器的关系如下图:

在这里插入图片描述


双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。
这里的加载器之间的子父关系不是继承,通常使用组合关系来复用父加载器的代码。
双亲委派模型并不是一个强制性约束力的模型而是Java设计者推荐给开发者的一种类加载器实现的最佳实践。

双亲委派模型的工作过程

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygxsj.html