【正文】Java类加载器( CLassLoader ) 死磕 4: 神秘的双亲委托机制

【正文】Java类加载器(  CLassLoader ) 死磕4: 

神秘的双亲委托机制

本小节目录

4.1. 每个类加载器都有一个parent父加载器

4.2. 类加载器之间的层次关系

4.3. 类的加载次序

4.4 双亲委托机制原理与沙箱机制

4.5. forName方法和loadClass方法的关系

4.6. 使用组合而不用继承

4.7. 各种不同的类加载途径

4.1.每个类加载器都有一个parent父加载器

每个类加载器都有一个parent父加载器,比如加载SystemConfig.class是由AppClassLoader完成,那么AppClassLoader也有一个父加载器,怎么样获取呢?很简单,通过getParent方法。

这里写了一个公共的函数,来取得一个类的加载器的双亲树。代码如下:

    /**

     * 迭代,显示class loader 和 父加载器
     */

    public static void showLoaderTree(ClassLoader loader) {

        while (loader != null) {

            Logger.info(loader.toString());

            loader = loader.getParent();

        }

    }

这个函数,不断循环,向上显示了父亲、父亲的父亲、父亲的父亲的父亲..,直到为空。

这样,就展示了一棵类加载器的双亲树。

使用上面的函数,演示代码如下:

 private static void loaderTreeDemo() throws ClassNotFoundException

    {

        String className = "com.crazymakercircle.config.SystemConfig";

        Class<?> aClass = Class.forName(className);

        ClassLoader aLoader=aClass.getClassLoader();

Logger.info("加载器:"+aLoader.toString());

        ClassLoaderUtil.showLoaderTree(aLoader);

    }

案例路径:com.crazymakercircle.classLoaderDemo.base.ParentTreeDemo

案例提示:无编程不创客、无案例不学习。切记,一定要跑案例哦

案例结果如下:

loaderTreeDemo |>  加载器:[email protected]

  showLoaderTree |>  [email protected]

  showLoaderTree |>  [email protected]

这个说明,当前加载器类型为AppClassLoader,而AppClassLoader父加载器类是ExtClassLoader。ExtClassLoader的父加载器,又是谁呢? 没有了打印。

parent为空表示什么意思呢?

我们先来梳理一下加载器之间的层次关系。

4.2. 类加载器之间的层次关系

下面展示一下Bootstrap 启动类加载器、Extention标准扩展类加载器和App应用类加载器三者之间的关系。

大致整理了如下类似的一幅图片:

每一个加载器看护一块自己的地盘。 启动Bootstrap 看护的是核心中的核心地盘。

前面讲到,ExtClassLoader的父加载器为空。而上图中,ExtClassLoader的父加载器是Bootstrap 启动类加载器。

实际上,如果一个加载器的parent为空,其父亲加载器就是Bootstrap 启动类加载器。

如果没有特别的设置,自定义加载的parent,默认为App应用加载器。

4.3. 类的加载次序

loadClass 关键源代码,已经在前面有介绍。

下面用一张图,对于类的加载次序,做进一步的介绍。

一般场景下,加载一个类,是从AppClassLoader开始的。

基本的步骤如下:

(1)AppClassLoader查找资源时,不是首先查看自己的地盘是否有这个字节码文件,而是直接委托给父加载器ExtClassLoader。当然,这里有一个假定,就是在AppClassLoader的缓存中,没有找到目标class。比方说,第一次加载一个目标类,这个类是不会在缓存的。

(2)ExtClassLoader查找资源时,也不是首先查看自己的地盘是否有这个字节码文件,而是直接委托给父加载器BootstrapClassLoader。

(3)如果父加载器BootstrapClassLoader在其地盘找到,并且加载成功,则直接返回了;反过来,如果在JVM的核心地盘——%sun.boot.class.path% 中没有找到。则回到ExtClassLoader查找其地盘。

(4)如果父加载器ExtClassLoader在自己的地盘找到,并且加载成功,也直接返回了;反过来,如果在ExtClassLoader的地盘——%java.ext.dirs% 中没有找到。则回到AppClassLoader自己的地盘。

(5)于是乎,逗了一大圈,终于回到了自己的地盘。还附带了两条件,就是前面的老大们没有搞定,否则也没有AppClassLoader啥事情了。

(6)AppClassLoader在自己的地盘找到,这个地盘就是%java.class.path%路径下查找。找到就返回。

(7)最终,如果没有找到,就抛出异常了。

这个过程,就是一个典型的双亲委托机制的一次执行流程。

什么是双亲委托机制呢?

4.4. 双亲委托机制原理与沙箱机制

双亲委派模型的的原理是:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父加载器反馈自己无法完全这个加载请求时,子加载器才会尝试自己去加载。

为什么要使用这种双亲委托模式呢?

因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

双亲委托机制,也就构成了JVM 的类的沙箱机制。

沙箱机制是由基于双亲委派机制上采取的一种JVM的自我保护机制,假设你要写一个java.lang.String 的类,由于双亲委派机制的原理,此请求会先交给Bootstrap试图进行加载,但是Bootstrap在加载类时首先通过包和类名查找rt.jar中有没有该类,有则优先加载rt.jar包中的类,因此就保证了java的运行机制不会被破坏。

4.5. forName方法和loadClass方法的关系

说到这里,顺便回答一下前面提出的一个问题。

前面提到,explicit显式方式,又分两种方式:

一是:java.lang.Class的forName()方法;

二是:java.lang.ClassLoader的loadClass()方法。

二者的区别和联系是什么呢?

首先看联系:

Class.forName使用的是调用者的类加载器loadClass()方法来加载类的。

其次看区别。

当调用Class.forName(String)载入class时执行,会完整的完成前面提到的五步工作,就是加载、验证、准备、解析、初始化。

如果调用ClassLoader.loadClass(String)载入class时,会执行加载、验证、准备、解析的前面四步,并不会执行第五步——初始化。这是,类的static块没有被执行。需要在第一次实例化时执行,比如第一次执行 Class.newInstance() 操作时。

4.6. 使用组合而不用继承

4.7. 各种不同的类加载途径

Java类不是一次性加载的,而是动态被加载到内存。这是java的一大特点,也称为运行时绑定,或动态绑定。

除了通过Java内置的三大加载器,从JVM中系统属性中设置的三大地盘加载Java类,还存在以下的获取Class文件途径: 

(1)从ZIP包中读取。很常见,最终成为日后JAR,WAR,EAR格式的基础。

(2)从网络中获取。这种场景典型的就是Applet。

(3)运行时计算生成。典型的情景就是java动态代理技术。

(4)从其他文件中生成。典型场景是JSP应用,即由JSP文件生成对应的Class类。

(5)从不方便加入到%java.class.path%其他的文件目录获取。

如何实现以上的途径呢?

具体的方法是:通自定义的类加载器,去加载其他途径的类。

原文地址:https://www.cnblogs.com/crazymakercircle/p/9819988.html

时间: 2024-10-05 05:41:48

【正文】Java类加载器( CLassLoader ) 死磕 4: 神秘的双亲委托机制的相关文章

Java类加载器( CLassLoader ) 死磕 3: 揭秘 ClassLoader抽象基类

[正文]Java类加载器(  CLassLoader ) 死磕3:  揭秘 ClassLoader抽象基类 3.1. 揭秘ClassLoader抽象基类 3.1.1. 类的加载分类:隐式加载和显示加载 java中类是动态加载的,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载.一是加快启动的速度,二是节约内存.如果一次性加载全部jar包的所有class,速度会很慢. 动态载入一个class类,有两种方式: (1) implicit隐式加载 即通过实例化才载入的特性来

java类加载器——ClassLoader

Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟类加载器打交道,但如果你想深入学习tomcat服务器的架构,它是必不可少的.所谓类加载器,就是用于加载Java类到Java虚拟机中,它负责读取Java字节码,并转换成java.lang.Class类的一个实例,使字节代码.class文件得以运行.一般类加载器负责根据一个指定的类找到对应的字节代码,然

浅析java类加载器ClassLoader

作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习. 本文从JDK提供的ClassLoader.委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一个简要的总结. JDK中提供的ClassLoader 1. Bootstrap ClassLoader Bootstrap加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib以及%JAVA_HOME%/jre/classes中的类,是最顶

深入理解 java类加载器ClassLoader

类加载器(class loader)用来加载 Java 类到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例.每个这样的实例用来表示一个 Java 类.通过此实例的 newInstance()方法就可以创建出该类的一个对象,也就是万能的Class对象. Jav

Java类加载器ClassLoader总结

JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中. 2.显式装载, 通过class.forname()等方法,显式加载需要的类 类加载的动态性体现: 一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是

Java类加载器ClassLoader的说明

(1)API文档内容如下: 类加载器是负责加载类的对象.ClassLoader 类是一个抽象类.如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据.一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的"类文件". 每个 Class 对象都包含一个对定义它的 ClassLoader 的引用. 数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建.数组类的类加载器由 Class.getClassLoader() 返回,该加载器与

潜水 java类加载器ClassLoader

类加载器(class loader)用于装载 Java 类到 Java 虚拟机中.一般来说.Java 虚拟机使用 Java 类的方式例如以下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类载入器负责读取 Java 字节代码.并转换成java.lang.Class类的一个实例.每一个这种实例用来表示一个 Java 类. 通过此实例的 newInstance()方法就能够创建出该类的一个对象,也就是万能的Class对象.

深入源码看java类加载器ClassLoader

ClassLoader类加载器是负责加载类的对象.ClassLoader 类是一个抽象类.如果给定类的二进制名称(即为包名加类名的全称),那么类加载器会试图查找或生成构成类定义的数据.一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的"类文件".java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class类的一个实例.除此之外,ClassLoad

Java类加载器 ClassLoader的解析

//参考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 类加载器基本概念 类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的.Java Applet 需要从远程下载 Java 类文件到浏览器中并执行.现在类加载器在 Web 容器和 O