如何从 JShell 获得更好的性能?

Skc*_*ssm 6 javascript java scripting jshell

背景

由于 Nashorn 在 JDK15 中被删除,我正在为我正在开发的应用程序寻找替代方案。我目前仅用于在 java swing 桌面应用程序中动态执行一些用户可定义的格式片段。

我并不是特别想向我的应用程序添加另一个库依赖项(例如 rhino)。如果可用的话,我愿意使用 nashorn 作为附加依赖项(这将使我不必重写代码,并确保与现有 js 片段的兼容性)。除了与《我的世界》相关的东西之外,我还没有看到它在任何地方都可用。

我不会切换到 Graal 虚拟机。

问题

我正在考虑使用 JShell(虽然不是 javascript,但大部分格式化代码非常相似),但我调用它的方式性能很糟糕:

try(JShell js = JShell.create())
{
    js.eval("public int add(int a, int b) { return a + b; }");
    for(int i = 0; i < 100; i++)
    {
        List<SnippetEvent> eval = js.eval("add(5,6)");
        eval.forEach(se -> {
            System.out.println(se.value());
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码中的 for 循环运行时间约为 6 秒(相比之下,nashorn 中的运行时间约为 11 微秒)。这对于我的应用程序来说不够快。

  • 有没有办法从 JSell 中获取类字节码,以便我可以使用反射直接执行该方法,而不是再次调用“eval”?

  • 有没有办法获取我在 JShell 中创建的方法的“方法句柄”?

  • 有没有什么方法可以创建一个函数,其行为是在 JShell 中定义的,但可以从“正常”java 中高性能地调用?

jbo*_*ann 3

我今天偶然发现了同样的性能问题,我想我找到了解决方案。归结为以下四个步骤:

  1. LocalExecutionControlProvider使用相同的类加载器在当前运行的 JVM 中启动 JShell
  2. 在类中定义一个静态字段,充当 JShell 和“普通”Java 代码之间的“共享变量”
  3. 使用新构造的 lambda 函数覆盖 JShell 内的静态字段
  4. 将构造函数的引用存储在“普通”Java 代码内的变量中(如果我们想通过这种方法构造多个函数,则这是必需的)

以下代码片段为您的示例实现了这种方法。在我的机器上,大约需要。通过 JShell 进行初始函数构建大约需要 350 毫秒 10_000 个后续函数调用需要 50ms。

package app;

import jdk.jshell.JShell;
import jdk.jshell.execution.LocalExecutionControlProvider;
import org.junit.jupiter.api.Test;

import java.util.function.BiFunction;

public class Debug {

    public static BiFunction<Integer, Integer, Integer> function = null;

    @Test
    public void debug() {
        JShell jShell = JShell.builder()
                .in(System.in).out(System.out).err(System.err)
                .executionEngine(new LocalExecutionControlProvider(), null)
                .build();
        jShell.eval("app.Debug.function = (a,b) -> a+b;");
        BiFunction<Integer, Integer, Integer> theFunction = Debug.function;
        for (int a = 0; a < 100; a++) {
            for (int b = 0; b < 100; b++) {
                assert theFunction.apply(a, b) == a + b;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)