You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Nashorn迁移至OpenJDK17+GraalVM时添加Bindings遇初始化异常

OpenJDK 17 升级 GraalVM 绑定Bindings时触发ExceptionInInitializerError问题排查

问题描述

从OpenJDK 17的Nashorn升级到GraalVM后,向脚本引擎作用域添加Bindings时抛出PolyglotException: java.lang.ExceptionInInitializerError异常。脚本引擎在类1的init方法中调用类2的静态方法初始化,后续在类3中执行脚本,怀疑多线程问题但无法定位根因。

相关代码

类1

public void init() {
    createScriptEngine();
}

类2

public static ScriptEngine createScriptEngine() {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine ret = manager.getEngineByName(engineName);
    if (ret instanceof GraalJSScriptEngine) {
        Bindings bindings = ret.getBindings(ScriptContext.ENGINE_SCOPE);
        bindings.put("polyglot.js.nashorn-compat", true);
    }
    return ret;
}

类3

private Object evaluateScript(ScriptEngine scriptEngine, String script) {
    Bindings bindings = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
    bindings.put(SCRIPT_VAR_t1, this);
    bindings.put(SCRIPT_VAR_t2, cTag);
    bindings.put(SCRIPT_VAR_t3, tAttribs);
    Object ret = scriptEngine.eval(script, scriptEngine.getContext());
    return ret;
}

排查方向

  • GraalVM引擎线程安全性差异:GraalJSScriptEngine的ENGINE_SCOPE Bindings并非线程安全结构,多线程共享同一引擎实例并修改Bindings时,会触发初始化异常。Nashorn的线程模型与GraalVM不同,之前的多线程逻辑升级后暴露问题。
  • 绑定对象的静态初始化异常:ExceptionInInitializerError通常关联静态代码块或静态成员初始化失败。检查绑定到Bindings中的对象(如this、cTag、tAttribs)所属类是否存在静态初始化错误,GraalVM访问这些对象时会触发异常。
  • 兼容性配置时机错误:类2中设置polyglot.js.nashorn-compat的时机过晚,GraalVM引擎可能已完成部分初始化,修改配置导致内部状态不一致。

修复建议

  1. 线程隔离ScriptEngine实例
    GraalJSScriptEngine不建议多线程共享,每个线程应持有独立实例。可通过ThreadLocal实现线程专属引擎:

    private static final ThreadLocal<ScriptEngine> ENGINE_HOLDER = ThreadLocal.withInitial(() -> createScriptEngine());
    
    public void init() {
        ScriptEngine engine = ENGINE_HOLDER.get();
        // 后续业务逻辑使用该引擎
    }
    
  2. 提前配置兼容性参数
    在创建引擎前设置兼容性参数,确保配置在引擎初始化阶段生效:

    public static ScriptEngine createScriptEngine() {
        // 创建引擎前设置系统属性
        System.setProperty("polyglot.js.nashorn-compat", "true");
        ScriptEngineManager manager = new ScriptEngineManager();
        return manager.getEngineByName(engineName);
    }
    

    若直接使用GraalVM API,可通过Context.Builder配置:

    Context context = Context.newBuilder("js")
        .option("js.nashorn-compat", "true")
        .build();
    ScriptEngine engine = context.getBindings("js").getEngine();
    
  3. 定位绑定对象的初始化异常
    捕获并打印ExceptionInInitializerError的根因,排查绑定对象所属类的静态初始化问题:

    try {
        Object ret = scriptEngine.eval(script, scriptEngine.getContext());
    } catch (PolyglotException e) {
        if (e.getCause() instanceof ExceptionInInitializerError) {
            Throwable rootCause = ((ExceptionInInitializerError) e.getCause()).getCause();
            rootCause.printStackTrace();
        }
    }
    
  4. 同步共享引擎的Bindings操作
    若必须共享引擎实例,在修改Bindings和执行脚本时添加同步锁:

    private Object evaluateScript(ScriptEngine scriptEngine, String script) {
        synchronized (scriptEngine) {
            Bindings bindings = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
            bindings.put(SCRIPT_VAR_t1, this);
            bindings.put(SCRIPT_VAR_t2, cTag);
            bindings.put(SCRIPT_VAR_t3, tAttribs);
            return scriptEngine.eval(script, scriptEngine.getContext());
        }
    }
    

内容的提问来源于stack exchange,提问作者virtuoso

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 23:09:21