从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_SCOPEBindings并非线程安全结构,多线程共享同一引擎实例并修改Bindings时,会触发初始化异常。Nashorn的线程模型与GraalVM不同,之前的多线程逻辑升级后暴露问题。 - 绑定对象的静态初始化异常:ExceptionInInitializerError通常关联静态代码块或静态成员初始化失败。检查绑定到Bindings中的对象(如
this、cTag、tAttribs)所属类是否存在静态初始化错误,GraalVM访问这些对象时会触发异常。 - 兼容性配置时机错误:类2中设置
polyglot.js.nashorn-compat的时机过晚,GraalVM引擎可能已完成部分初始化,修改配置导致内部状态不一致。
修复建议
线程隔离ScriptEngine实例
GraalJSScriptEngine不建议多线程共享,每个线程应持有独立实例。可通过ThreadLocal实现线程专属引擎:private static final ThreadLocal<ScriptEngine> ENGINE_HOLDER = ThreadLocal.withInitial(() -> createScriptEngine()); public void init() { ScriptEngine engine = ENGINE_HOLDER.get(); // 后续业务逻辑使用该引擎 }提前配置兼容性参数
在创建引擎前设置兼容性参数,确保配置在引擎初始化阶段生效: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();定位绑定对象的初始化异常
捕获并打印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(); } }同步共享引擎的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
相关产品推荐
相关产品推荐

