用同一个GroovyClassLoader加载的Class真的无法回收吗

源码地址https://gitee.com/mr_wenpan/basis-enhance/tree/master/enhance-boot-groovy-engine

一、问题描述

  • 最近需要使用到groovy,于是进行了一番调研,看到网上有个很常见的说法,如下:
    • 如果一个项目里使用同一个GroovyClassLoader来加载groovy脚本(将groovy脚本加载为Class对象并放入方法区)。那么根据Class从方法区被卸载的理论如下:
      • 该Class 的所有实例都已经被回收
      • 加载该类的classLoader已经被回收
      • 该Class 没有被引用
    • 所以得出结论,如果使用同一个GroovyClassLoader来加载groovy脚本,那么这些脚本的Class无法被卸载出方法区,因为加载这些Class的ClassLoader持有着这些Class的引用,所以这些Class无法被卸载。如果频繁的使用GroovyClassLoader来编译脚本为Class会造成方法区OOM
  • 对于以上说法,我个人持怀疑态度,GroovyClassLoader加载的Class真的不能被卸载出方法区吗?所以进行了如下验证

二、验证

1、验证类

public class GroovyCompiler implements DynamicCodeCompiler {

    private static final Logger LOG = LoggerFactory.getLogger(GroovyCompiler.class);
		// 这里新增一个公用的类加载器,每个脚本都通过这个类加载器来加载
    private static GroovyClassLoader groovyClassLoader = new GroovyClassLoader();

    @Override
    public Class<?> compile(String code, String name) {
        GroovyClassLoader loader = getGroovyClassLoader();
        LOG.warn("Compiling filter: " + name);
        return (Class<?>) loader.parseClass(code, name);
    }

    @Override
    public Class<?> compile(ScriptEntry scriptEntry) {
        GroovyClassLoader loader = getGroovyClassLoader();
        return loader.parseClass(scriptEntry.getScriptContext(),
                GroovyCompiler.class.getSimpleName() + scriptEntry.getName());
    }

    public GroovyClassLoader getGroovyClassLoader() {
			  // 使用同一个加载器来加载
        return groovyClassLoader;
    }
}

2、压测验证

  • 压测工具:jmeter
①、项目启动后方法区Class个数

②、项目启动后方法区内存

③、压测方法区Class个数

④、压测方法区内存

⑤、总结分析
  • 通过上面测试结果来看,由同一个GroovyClassLoader对象加载到方法区的Class其实是被回收了的,方法区的内存占用也不会持续上升
  • 那么是不是说明由一个ClassLoader加载到方法区的Class可以在加载他的ClassLoader未被卸载前,自身可以被从方法区卸载呢?结论是否定的!!!

三、分析总结

由上面的分析可以看到,由同一个GroovyClassLoader对象加载的Class,在加载他的ClassLoader没有被卸载前,自己竟然可以被卸载出方法区????这是啥原因呢?于是debug了一下源码!!!

1、在加载Class后打印该Class的ClassLoader
Class<?> aClass = dynamicCodeCompiler.compile(scriptEntry);
// 打印加载aClass的类加载器
System.out.println("aClass = " + aClass + "  ClassLoader = " + aClass.getClassLoader());
2、debug GroovyClassLoader的加载过程
  • 可以看到虽然使用的是同一个GroovyClassLoader来加载脚本为Class,但是GroovyClassLoader对象里每次加载都会创建一个新的ClassLoader去加载脚本
  • 所以并不是GroovyClassLoader去加载的脚本,而是由GroovyClassLoader每次都创建的一个类型为【InnerLoader】的类加载器去加载脚本,所以由【GroovyClassLoader】加载的脚本Class是可以被卸载出方法区的,即使【GroovyClassLoader】没有被卸载,只要加载该Class的【InnerLoader类加载器】被卸载了就行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值