JVM - 彻底理解打破双亲委派机制

简介: JVM - 彻底理解打破双亲委派机制

20200611235850703.png

Pre 双亲委派


JVM-白话聊一聊JVM类加载和双亲委派机制源码解析

JVM - 自定义类加载器


何为打破双亲委派


举个例子 有个类 Artisan


我们希望通过自定义加载器 直接从某个路径下读取Artisan.class . 而不是说 通过自定义加载器 委托给 AppClassLoader ------> ExtClassLoader ----> BootClassLoader 这么走一遍,都没有的话,才让自定义加载器去加载 Artisan.class . 这么一来 还是 双亲委派。


我们期望的是 Artisan.class 及时在 AppClassLoader 中存在,也不要从AppClassLoader 去加载。


说白了,就是 直接让自定义加载器去直接加载Artisan.class 而不让它取委托父加载器去加载,不要去走双亲委派那一套。


我们知道 双亲委派的机制是在ClassLoader # loadClass方法中实现的,打破双亲委派,那我们是不是可以考虑从这个地方下手呢?


如何打破双亲委派

核心: 重写ClassLoader#loadClass方法


演示

刚才的思路是对的,要打破它,那就搞loadClass方法。


重写loadClass方法呗。

我们基于 JVM - 自定义类加载器 再来搞一搞

需要再此基础上 重写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) {
                }
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类
                    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;
        }
    }


那打破它,那我们就不要委托父加载器了呗,直接去findClass 不就好了?

我们把loadClass方法的源码copy过来 把双亲委派的部分代码去掉吧,走 改下


重写 ClassLoader#loadClass

public class MyClassLoaderTest {
    static class MyClassLoader extends ClassLoader {
        private String classPath;
        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name
                    + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }
        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) {
                    c = findClass(name);
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }
    }
   protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // 尝试加载,不存在直接去findClass ,不走委托父类
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    c = findClass(name);
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }


运行下


20200612002901268.png

失败原因探究


略微尴尬, Object.class 找不到 。 为啥 呢? 你加载Boss1的时候, Boss1的父类也需要被加载, 你又把双亲委派给关了, 这个自定义的加载器在本地路径下是找不到Object.class的 。


咋办? 放到自定义的加载器加载的路径下 ?


-----> 其实是不行的,Object 谁能篡改的了啊 ,Object只能由引导类加载器来加载。


临时解决办法


所以换个思路 ,自己的类路径下的对象走我自己的classLoader, 其他的类 还是走双亲委派

 if ("com.gof.facadePattern.Boss1".equals(name)){
      c = findClass(name);
 }else{
     // 交由父加载器去加载
      c = this.getParent().loadClass(name);
 }


验证是否成功


这个时候我们在AppClassLoader加载的路径下 再创建个Boss1 (如果走的还是双亲委派,那加载器肯定还是AppClassLoader)

看 是不是这个Boss1 还是被自定义的ClassLoader加载,如果是,说明打破成功。


应用下新建Boss1类

自定义加载路径D:/artisan/com/gof/facadePattern下保留Boss1.class

验证

20200612004204666.png


输出结果

20200612004418425.png


OK,双亲委派机制 打破成功。

这个在tomcat类加载机制中非常重要,所以需要彻底明白这一点。


相关文章
|
7月前
|
存储 缓存 Java
金石原创 |【JVM盲点补漏系列】「并发编程的难题和挑战」深入理解JMM及JVM内存模型知识体系机制(1)
金石原创 |【JVM盲点补漏系列】「并发编程的难题和挑战」深入理解JMM及JVM内存模型知识体系机制(1)
88 1
|
4月前
|
安全 前端开发 Java
【JVM的秘密揭秘】深入理解类加载器与双亲委派机制的奥秘!
【8月更文挑战第25天】在Java技术栈中,深入理解JVM类加载机制及其双亲委派模型是至关重要的。JVM类加载器作为运行时系统的关键组件,负责将字节码文件加载至内存并转换为可执行的数据结构。其采用层级结构,包括引导、扩展、应用及用户自定义类加载器,通过双亲委派机制协同工作,确保Java核心库的安全性与稳定性。本文通过解析类加载器的分类、双亲委派机制原理及示例代码,帮助读者全面掌握这一核心概念,为开发更安全高效的Java应用程序奠定基础。
98 0
|
7月前
|
Java 关系型数据库 MySQL
【JVM】JDBC案例打破双亲委派机制
【JVM】JDBC案例打破双亲委派机制
181 4
|
2月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
75 3
|
3月前
|
Arthas Java 测试技术
JVM —— 类加载器的分类,双亲委派机制
类加载器的分类,双亲委派机制:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器;JDK8及之前的版本,JDK9之后的版本;什么是双亲委派模型,双亲委派模型的作用,如何打破双亲委派机制
JVM —— 类加载器的分类,双亲委派机制
|
4月前
|
开发者 C# Windows
WPF布局大揭秘:掌握布局技巧,轻松创建响应式用户界面,让你的应用程序更上一层楼!
【8月更文挑战第31天】在现代软件开发中,响应式用户界面至关重要。WPF(Windows Presentation Foundation)作为.NET框架的一部分,提供了丰富的布局控件和机制,便于创建可自动调整的UI。本文介绍WPF布局的基础概念与实现方法,包括`StackPanel`、`DockPanel`、`Grid`等控件的使用,并通过示例代码展示如何构建响应式布局。了解这些技巧有助于开发者优化用户体验,适应不同设备和屏幕尺寸。
127 0
|
4月前
|
存储 监控 算法
深入解析JVM内部结构及GC机制的实战应用
深入解析JVM内部结构及GC机制的实战应用
|
5月前
|
存储 前端开发 Java
(二)JVM成神路之剖析Java类加载子系统、双亲委派机制及线程上下文类加载器
上篇《初识Java虚拟机》文章中曾提及到:我们所编写的Java代码经过编译之后,会生成对应的class字节码文件,而在程序启动时会通过类加载子系统将这些字节码文件先装载进内存,然后再交由执行引擎执行。本文中则会对Java虚拟机的类加载机制以及执行引擎进行全面分析。
103 0
|
6月前
|
监控 算法 Java
深入理解Java虚拟机:垃圾收集机制的奥秘
【6月更文挑战第17天】在Java的世界,垃圾收集(GC)是保持内存健康不可或缺的一环。本文将揭开JVM垃圾收集的神秘面纱,探索其原理、算法及调优策略,帮助开发者更好地理解和掌握这一关键技术,确保Java应用的性能与稳定性。
44 5
|
5月前
|
监控 算法 Java
Java虚拟机垃圾收集机制深度解析
在Java的世界中,垃圾收集是确保内存管理高效运行的关键机制之一。本文将深入探讨Java虚拟机的垃圾收集机制,包括其工作原理、常见的垃圾收集算法以及调优实践。我们将基于最新的研究数据和实验结果,提供对垃圾收集器性能的比较分析,并讨论如何根据不同应用场景进行优化。通过逻辑严密的分析,我们旨在为Java开发者提供实用的指导,以帮助他们更好地理解和掌握这一关键技术。