概述
背景
groovy用来和java集成,作为动态规则引擎使用,是非常不错的一个选择。简单来说,就是用java来执行一段groovy代码。可以通过一个简单的数据库配置,来动态的执行某段脚本。这样就可以实时得更改脚本,java就可以动态调用这段代码,从而达到灵活的在线变换的规则引擎。
坑
假如不做任何优化的话,那么每次java执行一次groovy脚本,都会动态生成一个class,将导致class越来越多,最终导致JVM进行perm区爆满的问题。
测试
用以下脚本循环执行groovy代码
while(true){
Binding binding = new Binding();
binding.setVariable("x", 10);
binding.setVariable("language", "Groovy");
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("return x*2");//反复执行这段groovy脚本
System.out.println(value);
}
class检测
通过jconsole可以看到,class的数量线性增加,到了一定数量后就触发GC导致应用异常。
1、为什么Groovy每执行一次脚本,都会生成一个脚本对应的class对象?
因为一个ClassLoader对于同一个名字的类只能加载一次,都由GroovyClassLoader加载,那么当一个脚本里定义了C这个类之后,另外一个脚本再定义一个C类的话,GroovyClassLoader就无法加载了。
2、为什么InnerLoader加载的对应无法通过gc清理掉?
大家都知道,JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载:1. 该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例;2. 加载该类的ClassLoader已经被GC;3. 该类的java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法。
在GroovyClassLoader代码中有一个class对象的缓存,进一步跟下去,发现每次编译脚本时都会在Map中缓存这个对象,即:setClassCacheEntry(clazz)。每次groovy编译脚本后,都会缓存该脚本的Class对象,下次编译该脚本时,会优先从缓存中读取,这样节省掉编译的时间。这个缓存的Map由GroovyClassLoader持有,key是脚本的类名,这就导致每个脚本对应的class对象都存在引用,无法被gc清理掉。
解释
每次groovy编译脚本的时候,都会生成一个名称为”script” + System.currentTimeMillis() + Math.abs(text.hashCode()) + “.groovy”的class对象。而此对象又会被map缓存起来,key就是刚才的名称,这样导致gc无法回收,从而导致fullgc
最后
以上就是温暖斑马为你收集整理的groovy与java集成的坑背景坑测试class检测的全部内容,希望文章能够帮你解决groovy与java集成的坑背景坑测试class检测所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复