jvm深度学习(18):类加载器和双亲委派机制

前言:类加载主要做五件事:加载、验证、准备、解析、初始化,而这些都需要类加载器来完成。

类加载器

前面说过类加载,而执行这个任务的就是类加载器,类加载主要做五件事:加载、验证、准备、解析、初始化。JDK为我们提供了三层类加载器分别是:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extention ClassLoader)、应用程序类加载器(Application ClassLoader)。除了这三种还有自定义类加载器(Custom ClassLoader),支持一些个性化的扩展功能。

 

启动类加载器

 Bootstrap ClassLoader,他处于加载器中最顶层,任何类的加载行为,都要经它过问。它的作用是加载核心类库,也就是 rt.jar、resources.jar、charsets.jar 等。当然这些 jar 包 的路径是可以指定的,-Xbootclasspath 参数可以完成指定操作。这个加载器是 C++ 编写的,随着 JVM 启动。

 

扩展类加载器

Extention ClassLoader,主要用于加载 lib/ext 目录下的 jar 包和 .class 文件。同样的,通过系统变量 java.ext.dirs 可以指定这个目录。这个加载器是个 Java 类,继承自 URLClassLoader。

 

应用程序类加载器

Application ClassLoader,这是我们写的 Java 类的默认加载器,有时候也叫作 System ClassLoader。一般用来加载 classpath 下的其他所有 jar 包和 .class 文件,我们写的代码, 会首先尝试使用这个类加载器进行加载。

 

自定义加载器

Custom ClassLoader,支持一些个性化的扩展功能。

 

双亲委派机制

提问:为什么要有双亲委派机制?

分析:如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。不仅如此,这将会给虚拟机的安全带来隐患。而JVM是用加载这个类的类加载器和这个类来确定唯一性。这就需要每次类加载时,都会由下而上的检查是否有被类加载器加载过。

双亲委派机制就是解决这个问题的。

图示:

 

        双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。
        使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类 java.lang.Object,它存放在 rt.jar 之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此 Object 类在 程序的各种类加载器环境中都是同一个类。

源码:

ClassLoader中的loadClass

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

 

 我们看到,当我们进行类加载时,它首先使用 parent 尝试进行类加载

parent 失 败后才轮到自己

同时,我们也注意到,这个方法是可以被覆盖的,也就是双亲委派机制并不一定生效。在特定的业务环境中,我们是需要对双亲委派进行隔离的,下一章节我们将对此进行详谈。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值