Nashorn绑定值数量超限致变量丢失/计数异常问题求助
Nashorn 15.4绑定变量数量超过448时丢失/计数异常的解决方案
问题背景
使用OpenJDK版本的Nashorn 15.4,在Java 21和Java 11环境下测试均出现同一问题:当通过engine.put()绑定的变量数量超过448时,部分绑定变量会丢失或计数不一致,首次运行正常但后续迭代出错。448个及以下变量时一切正常,449个及以上触发问题。
复现代码
import javax.script.ScriptContext; import javax.script.ScriptEngine; public class JsTest { public static void main(String[] args) throws Exception { int numberOfBindingVars = Integer.parseInt(args[0]); ScriptEngine engine; for(int i=0; i<10; i++) { engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine(); for(int k=0;k<numberOfBindingVars;k++) { engine.put("var"+k,"1963"); } System.out.println(String.format("Bindings %d", engine.getBindings(ScriptContext.ENGINE_SCOPE).size())); for(int k=0;k<numberOfBindingVars;k++) { if(engine.get("var"+k)==null) { System.out.println("Missing var"+k); } } } } }
问题原因
Nashorn内部对ENGINE_SCOPE的绑定变量有一个隐式的固定槽位限制(默认448个)。当绑定变量数量超过这个值时,超出部分会被存入后备哈希表,但引擎在后续的变量检索、计数逻辑中存在Bug,导致超出槽位的变量无法被正确访问,或绑定计数出现偏差。
解决方案
1. 用单个对象封装所有绑定变量(推荐)
将所有需要绑定的变量放入一个Java Map或自定义对象中,仅绑定这个容器对象到引擎,避开单个变量数量的限制。示例代码:
import javax.script.ScriptEngine; import java.util.HashMap; import java.util.Map; public class JsTest { public static void main(String[] args) throws Exception { int numberOfBindingVars = Integer.parseInt(args[0]); ScriptEngine engine; for (int i = 0; i < 10; i++) { engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine(); Map<String, String> metadataVars = new HashMap<>(); // 将所有变量放入Map for (int k = 0; k < numberOfBindingVars; k++) { metadataVars.put("var" + k, "1963"); } // 仅绑定Map对象 engine.put("metadata", metadataVars); // 验证变量 Map<String, String> boundVars = (Map<String, String>) engine.get("metadata"); System.out.println(String.format("Bindings count: %d", boundVars.size())); for (int k = 0; k < numberOfBindingVars; k++) { if (boundVars.get("var" + k) == null) { System.out.println("Missing var" + k); } } } } }
2. 使用自定义Bindings实例替代逐个put
创建SimpleBindings实例,将所有变量放入后一次性设置到引擎,避免逐个put触发的内部槽位限制逻辑。示例:
import javax.script.Bindings; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.SimpleBindings; public class JsTest { public static void main(String[] args) throws Exception { int numberOfBindingVars = Integer.parseInt(args[0]); ScriptEngine engine; for (int i = 0; i < 10; i++) { engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine(); Bindings customBindings = new SimpleBindings(); // 批量放入变量到自定义Bindings for (int k = 0; k < numberOfBindingVars; k++) { customBindings.put("var" + k, "1963"); } // 设置到引擎 engine.setBindings(customBindings, ScriptContext.ENGINE_SCOPE); // 验证 System.out.println(String.format("Bindings %d", engine.getBindings(ScriptContext.ENGINE_SCOPE).size())); for (int k = 0; k < numberOfBindingVars; k++) { if (engine.get("var" + k) == null) { System.out.println("Missing var" + k); } } } } }
3. 迁移到GraalVM JavaScript引擎
Nashorn已在Java 15后被标记为废弃,GraalVM JavaScript是官方推荐的替代方案,对大量绑定变量的支持更稳定,性能也更优。
验证结果
上述两种Nashorn兼容方案在测试1000个绑定变量时,所有迭代均能正确显示绑定数量,无变量丢失情况。
内容的提问来源于stack exchange,提问作者Paul Taylor
相关产品推荐
相关产品推荐

