bvd*_*vdb 5 java scriptengine nashorn
我的应用程序使用a ScriptEngine为我的最终用户提供插件功能.
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
Run Code Online (Sandbox Code Playgroud)
每当用户对其脚本进行更改时,应用程序都会通过新实例替换引擎实例.
String newScript = ...;
engine = engineManager.getEngineByName("nashorn");
engine.eval(newScript);
Run Code Online (Sandbox Code Playgroud)
两个密切相关的问题:
我应该尝试重用引擎并对它们执行某种操作clear()吗?
如果我只是用新实例替换我的引擎,我应该以某种方式处理前一个实例,以避免内存泄漏吗?(例如,我可以想象用户可以设法创建一个启动线程的脚本.)
问题是,我找不到任何看起来像a clear()或a的方法dispose().这是否意味着我目前的做法是正确的?
我只是想分享自己测试的内容。这完全有道理,但对于那些仍存疑问的人:如果仅替换引擎实例,则创建的线程确实会继续运行:
public static void main(String[] args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
String script =
"new java.lang.Thread(function() {\n" +
" for(;;) {" +
" print('Here\\'s Johnny !');" +
" java.lang.Thread.sleep(1000);" +
" }\n" +
"}).start();";
ScriptEngine engine = manager.getEngineByName("nashorn");
try {
engine.eval(script);
} catch (ScriptException e) {
e.printStackTrace();
}
// replace engine
engine = manager.getEngineByName("nashorn");
engine.eval("print('please, make it stop!!!');");
// please collect !!!
System.gc();
}
Run Code Online (Sandbox Code Playgroud)
输出:
这是约翰尼!
请停下来!!!
这是约翰尼!
这是约翰尼!
这是约翰尼!
...
我猜想垃圾回收器可以清除脚本,但不能清除上下文之外的动作。我认为创建的线程甚至都没有以任何方式链接到脚本(即不在其范围内)。因此,我认为jvm不可能检测或确定这些线程是否链接到已替换的脚本,并且可能会停止也可能不会停止。
但这对于一个stackoverflow问题来说太过分了。让我们仅关注处理/清除绑定(即ScriptContext)的能力。
一种可能的解决方案是缩小可用功能。这里有几种避免创建线程的方法:
以下内容禁用了所有Java功能:
// the option -nj is short for --no-java
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine("-nj");
Run Code Online (Sandbox Code Playgroud)
但是您也可以使用禁用特定的类ClassFilter。
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine((className) -> {
if ("java.lang.Thread".equals(className)) return false;
if ("java.lang.Runnable".equals(className)) return false;
if ("java.util.Timer".equals(className)) return false;
if (className.startsWith("java.util.concurrency")) return false;
if (className.startsWith("javafx")) return false;
if (className.startsWith("javax.swing")) return false;
if (className.startsWith("java.awt")) return false;
return true;
});
Run Code Online (Sandbox Code Playgroud)
注意:一旦定义了一个ClassFilter反射类,该类也会自动被阻止。因此,您不必显式地阻止那些软件包。