ScriptEngine清除并处置

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().这是否意味着我目前的做法是正确的?

Att*_*edi 5

您可以使用单个引擎实例,但使用单独的Bindings对象.Bindings充当顶级程序环境,因此如果您想将脚本评估为基本上是"新的全局范围",那么您可以这样做.查看有关如何执行此操作的javax.script API文档.您既可以使用第二个参数ScriptEngine.eval,也可以使用第二个参数.BindingsScriptContext

即使没有从上一次评估中幸存下来的脚本代码,您也将节省一些初始化时间,因为脚本引擎已经预定义了各种JavaScript数据持有者类和属性映射("隐藏类").

另外:是的,一切都是垃圾收集.不需要明确的"处置"API.


bvd*_*vdb 5

我只是想分享自己测试的内容。这完全有道理,但对于那些仍存疑问的人:如果仅替换引擎实例,则创建的线程确实会继续运行

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)的能力。

在nashorn脚本中阻止Java线程:

一种可能的解决方案是缩小可用功能。这里有几种避免创建线程的方法:

以下内容禁用了所有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反射类,该类也会自动被阻止。因此,您不必显式地阻止那些软件包。