java运行groovy脚本内存问题及解决
一、问题重现工具类:public class GroovyUtil {public static Object engine(String filePath, String fileName, Map<String,Object> variable) {Object result;GroovyScriptEngine engine = null;try{Binding binding
·
专题导航
一、java调用groovy及groovy中如何使用springBean
二、java运行groovy脚本内存问题及解决
三、java运行groovy脚本并发问题及解决
四、java运行groovy工具类
一、问题重现
工具类:
public class GroovyUtil {
public static Object engine(String filePath, String fileName, Map<String,Object> variable) {
Object result;
GroovyScriptEngine engine = null;
try{
Binding binding = new Binding();
variable.entrySet().stream().filter(entry-> StrUtil.isNotBlank(entry.getKey())&& ObjectUtil.isNotEmpty(entry.getValue()))
.forEach(entry-> binding.setVariable(entry.getKey(),entry.getValue()));
engine = new GroovyScriptEngine(filePath);
result = engine.run(fileName, binding);
}catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("路径不存在");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("执行文件发生错误");
} finally {
//清除缓存(每次生成的class文件并不一致)
if (engine!=null){
engine.getGroovyClassLoader().clearCache();
}
}
return result;
}
}
每次需要执行groovy脚本的时候,都需要调用engine方法。观察engine.run()方法:
public Object run(String scriptName, Binding binding) throws ResourceException, ScriptException {
return createScript(scriptName, binding).run();
}
每次执行都会创建script对象。预想的是每次执行完都清除缓存,保证传入的参数正确。
进行测试:
通过JProfiler发现在执行这一百次的时候,虚拟机分配最大内存1024M的情况下竟然GC了两次:
发现GroovyClassLoader().clearCache()并未起作用。
二、优化
问题如上所说,engine.run()方法每次执行都会创建script对象,则考虑把script对象存储起来,不必每次去创建该对象,每次调用方法时,修改script的绑定参数。
public class GroovyUtil {
private static Map<String, Script> scriptMap = new ConcurrentHashMap<>();
public static Object engine(String filePath, String fileName, Map<String, Object> variable) {
Binding binding = new Binding();
if (CollectionUtil.isNotEmpty(variable)) {
variable.entrySet().stream().filter(entry -> StrUtil.isNotBlank(entry.getKey()) && ObjectUtil.isNotEmpty(entry.getValue()))
.forEach(entry -> binding.setVariable(entry.getKey(), entry.getValue()));
}
Script script = getScriptInstance(filePath, fileName);
script.setBinding(binding);
return script.run();
}
private static Script getScriptInstance(String filePath, String fileName) {
File file = new File(filePath + File.separator + fileName);
String md5Hex = DigestUtil.md5Hex(file);
if (scriptMap.containsKey(md5Hex)) {
return scriptMap.get(md5Hex);
} else {
Script script = null;
try {
GroovyScriptEngine engine = new GroovyScriptEngine(filePath);
script = engine.createScript(fileName, new Binding());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件不存在");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("生成script文件失败");
}
scriptMap.put(md5Hex, script);
return script;
}
}
}
JProfiler测试:内存几乎无变化
内存逐渐递增是因为项目中其他代码块在运行
三、继续优化
此时如果文件内容改变,则对文件md5与之前的md5值并不会相同,之钱创建的script并不会继续使用。而将其维持在map中,会影响GC将其回收(可达性)。
public class GroovyUtil {
private static final Map<String, Md5Script> scriptMap = new ConcurrentHashMap<>();
public static Object engine(String filePath, String fileName, Map<String, Object> variable) {
Binding binding = new Binding();
if (CollectionUtil.isNotEmpty(variable)) {
variable.entrySet().stream().filter(entry -> StrUtil.isNotBlank(entry.getKey()) && ObjectUtil.isNotEmpty(entry.getValue()))
.forEach(entry -> binding.setVariable(entry.getKey(), entry.getValue()));
}
Script script = getScriptInstance(filePath, fileName);
script.setBinding(binding);
return script.run();
}
private static Script getScriptInstance(String filePath, String fileName) {
String fileAbPath = filePath+File.separator+fileName;
File file = new File(fileAbPath);
String md5Hex = DigestUtil.md5Hex(file);
Md5Script md5Script = scriptMap.getOrDefault(fileAbPath,null);
if (md5Script!=null&&md5Hex.equals(md5Script.getMd5())) {
return md5Script.getScript();
} else {
Script script = null;
try {
GroovyScriptEngine engine = new GroovyScriptEngine(filePath);
script = engine.createScript(fileName, new Binding());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("文件不存在");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("生成script文件失败");
}
scriptMap.put(fileAbPath, new Md5Script(md5Hex,script));
return script;
}
}
@Data
private static class Md5Script{
private String md5;
private Script script;
public Md5Script(String md5, Script script) {
this.md5 = md5;
this.script = script;
}
}
}
更多推荐
所有评论(0)