自定义类加载器_jvm超详细探索自定义类加载器(值得收藏)

原创:鲁班学院子牙老师 微信公众号搜索启明南

84fe66c2684ade8e2fbe6eb6b7d014b5.png

如果你想看懂本篇文章,需要你对类加载器有一定的了解。如JVM自带的类加载器、双亲委派、自定义类加载器、类加载每个阶段做了什么……如果你对这些知识还有夹生的地方,建议先去补充一下,不然只能达到劝退的效果。

jvm调优视频地址:https://www.ixigua.com/i6845139758574731787/

在平时开发中,很少会去自定义类加载器,所以以前对这块也只是了解。如今作为一名老师,自然需要对每个重要知识点达到颗粒剂认知。在研究这块知识点时,发现这块知识点并不是我想的那么简单,而是存在着很多很多容易产生错误认知的地方以及不太容易理解的地方。为了详尽搞情况这些知识点,写了很多代码测试,写点文章记录下。

后面的自定义类加载器代码测试时都加载这个类

package com.qimingnan.classload;class Classloader_1_A {}

测试代码较多,文章中我只贴核心部分。相关的代码需要的童鞋可关注后私信:课堂演示代码

JVM自带的类加载器

jvm自带的类加载器相信大多数童鞋都知道,这边我也只是打算稍微提一下,因为关于这块的认识,很多童鞋也是存在误区的。

JVM自带的类加载器有三个:引导类加载器、扩展类加载器、应用类加载器。这三者之间是逻辑上的父子关系,通过parent指针关联。不要错误的理解成一种继承关系,看下openjdk源码就知道了,启动类加载器是纯C++编写的,扩展类加载器、应用类加载器都是继承自类URLClassLoader。

JVM自带的三个类加载器在运行期都只有一个实例,但是自定义类加载器你可以创建无数个。但是建议越少越好,会带来碎片化问题。

为什么说会有碎片化问题呢?这跟JVM的处理机制有关。不同的类加载器加载的类在元空间中是如何存储的呢?每个加载器在元空间中都能获得一份独有的空间,不同的类加载器加载的类都是存放在这个空间中。这就解释了同一个类,如果同一个类加载器去加载只会加载一次,多个类加载器去加载就会加载多次。关于这个空间可能童鞋们又有很多问题,如这个空间初始多大?引用存放在哪?碎片问题如何解决?这些问题后面写文章分享或在VIP课上揭晓。

ed5e8b3ab8848055539a770430fc27be.png

自定义类加载器

想自定义一个类加载器是一件很简单的事情,继承类ClassLoader就可以了。但现实中,关于自定义类加载器,还是有很多细节值得我们去细细品味的。

一、只继承类ClassLoader,不override任何方法,这样的类加载器能否正常运行。上代码

public class Classloader_4 extends ClassLoader {    public static void main(String[] args) {        Classloader_4 classloader = new Classloader_4();        try {            Class> clazz = classloader.loadClass("com.qimingnan.classload.Classloader_1_A");            Object obj = clazz.newInstance();            System.out.println(obj);        } catch (Exception e) {            e.printStackTrace();        }    }    @Override    protected Class> findClass(String className) throws ClassNotFoundException {        System.out.println("Classloader_2 findClass");        return null;    }}

因为双亲委派机制,自定义类加载器加载类Classloader_1时会将这个加载请求依次上抛给引导类加载器、扩展类加载器、应用类加载器。引导类加载器、扩展类加载器肯定加载不到这个类的。如果AppClassLoader能加载类Classloader_1则完成加载;如果AppClassLoader加载不到,就会由自定义的类加载器Classloader_4 去加载,根据函数调用链,最终会调用findClass方法。

二、如何让自定义的类加载器能够加载到类Classloader_1,上代码

public static void main(String[] args) {    Classloader_2 classloader = new Classloader_2();    classloader.setFilepath("/home/ziya/Documents/java-text/");        ……}@Overrideprotected Class> findClass(String className) throws ClassNotFoundException {    System.out.println("Classloader_2 findClass");    byte[] data = getData(className.replace('.', '/'));    return defineClass(className, data, 0, data.length);}private byte[] getData(String name) {    File file = new File(filepath + name + SUFFIX);        ……    return null;}

findClass调用getData方法根据class文件的绝对路径将class文件读入内存,然后调用defineClass方法将字节码文件转为Class对象并返回。

三、前面有说每个类加载器在元空间中都会分得一块独有的空间,上代码证明下

public class Classloader_4 extends ClassLoader {    public static void main(String[] args) throws Exception {        Classloader_4 classloader1 = new Classloader_4();        Class> clazz1 = classloader1.loadClass("com.qimingnan.classload.Classloader_1_A");        System.out.println("clazz1 hashcode: " + clazz1.hashCode());        Classloader_4 classloader2 = new Classloader_4();        Class> clazz2 = classloader2.loadClass("com.qimingnan.classload.Classloader_1_A");        System.out.println("clazz2 hashcode: " + clazz2.hashCode());    }   ……}

运行结果:

clazz1 hashcode: 460141958clazz2 hashcode: 460141958

hashcode即Class对象的内存地址,可以看到两个类加载器返回的是同一个Class对象。为什么会这样呢?因为并非是由这两个类加载器去加载的,而是由两个类加载器的父类加载器AppClassLoader加载的。如果是由这两个类加载器加载呢?看结果:

Classloader_4 findClassclazz1 hashcode: 1956725890Classloader_4 findClassclazz2 hashcode: 21685669

可以看到,因为是两个不同的类加载器,返回的Class对象的地址也是不一样的。这也证明了不同的类加载器有其独享的内存区域。

测试思路:让AppClassLoader加载不到类Classloader_1_A即可。我是将classes目录下的class文件删掉了。删掉后如果后面不会再生成,执行下maven clean后重试。

结语

本文讨论了自定义类加载器的四种情况的细节:

  1. 使用一个自定义类加载器去加载类,该加载器的父类加载器也能加载到这个类
  2. 使用一个自定义类加载器去加载类,该加载器的父类加载器无法加载到这个类
  3. 使用多个自定义类加载器去加载类,加载器的父类加载器也能加载到这个类
  4. 使用多个自定义类加载器去加载类,加载器的父类加载器无法加载到这个类

这四种情况就包含了所有的场景吗?当然没有!比如多个自定义类加载器之间存在父子关系又有哪些不同。但是万变不离其宗,这四种情况是最基础的情况,深刻理解这四种情况,后续接触到其他情况就能举一反三理解之。建议看完文章的童鞋实践实践,绝知此事要躬行。

jvm调优视频:https://www.ixigua.com/i6845139758574731787/

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值