当前位置: 首页 > 知识库问答 >
问题:

来自Nashorn的IllegalArgumentException异常——它是Java 8中的一个bug吗?

卫嘉言
2023-03-14

我使用Nashorn javascript引擎来评估java应用程序中编写的所有服务器端javascript代码。为了提高性能,我在启动和评估时使用spring初始化JsEngine

我无法找到问题的根本原因。

@Component
public class JsEngine {

    private ScriptEngine scriptEngine;

    @PostConstruct
    public void init() throws ScriptException, IOException{
        scriptEngine = new ScriptEngineManager().getEngineByName("nashorn");
        this.cacheAllCoreEngines();
        for(String key: defaultEngineSource.keySet()){
            scriptEngine.eval(defaultEngineSource.get(key));
        }
    }

    private void cacheAllCoreEngines()throws IOException{
       //read all core files such as mustache, etc. 
       defaultEngineSource.put("mustache",  FileUtil.readFileFromDisk("<actual path..>/mustache.js"));
    }

    public Object eval(String source) throws  ScriptException{
            .... code to handle exceptions 
            return scriptEngine.eval (source);
    }

}

JsEngine使用如下所示,

public class AppRendererImpl implements  AppRenderer {

    @Autowired
    JsEngine jsEngine;

    public String render(){
     ....
     .... //Read source from disk or cache
     jsEngine.eval(source);....
    }     
}

几个渲染周期后的异常,

例外情况:
java。lang.IllegalArgumentException:目标和筛选器类型在java上不匹配:(ScriptObject)对象,(Object)对象
。lang.invoke。方法论。java上的newIllegalArgumentException(MethodHandleStatics.java:115)
。lang.invoke。方法手柄。java上的filterArgument(MethodHandles.java:2416)
。lang.invoke。MethodHandles。jdk上的filterArguments(MethodHandles.java:2403)
。纳肖恩。内部的查找。MethodHandleFactory$StandardMethodHandleFunctionality。jdk上的filterArguments(MethodHandleFactory.java:277)
。纳肖恩。内部的运行时。WithObject。jdk上的过滤器(WithObject.java:270)
。纳肖恩。内部的运行时。WithObject。fixExpressionCallSite(WithObject.java:249)
位于jdk。纳肖恩。内部的运行时。WithObject。在jdk上查找(WithObject.java:169)
。纳肖恩。内部的运行时。连接器NashornLinker。jdk上的GetGuardInvocation(NashornLinker.java:96)
。内部的dynalink。支持CompositeTypebasedGuardingDynamics。GetGuardInvocation(compositeTypeBasedGuardingDynamicScand.java:176)
在jdk上。内部的dynalink。支持复合防护。jdk上的GetGuardInvocation(compositeGuardingDynamicScand.java:124)
。内部的dynalink。支持LinkerServiceSimp。jdk上的GetGuardInvocation(LinkerServiceSiml.java:144)
。内部的dynalink。动态熟料。在jdk上重新链接(dynamicsolder.java:232)
。纳肖恩。内部的脚本。脚本$^eval_jdk的L6$_L8(:21)
。纳肖恩。内部的脚本。脚本$^eval_jdk的L6$_L40(:41)
。纳肖恩。内部的脚本。脚本$^eval。jdk上的runScript(:1)
。纳肖恩。内部的运行时。ScriptFunctionData。在jdk上调用(ScriptFunctionData.java:498)
。纳肖恩。内部的运行时。脚本函数。在jdk上调用(ScriptFunction.java:206)
。纳肖恩。内部的运行时。脚本运行时。在jdk上应用(ScriptRuntime.java:378)
。纳肖恩。应用程序编程接口。脚本编写。纳什启动了引擎。evalImpl(NashornScriptEngine.java:546)
位于jdk。纳肖恩。应用程序编程接口。脚本编写。纳什启动了引擎。evalImpl(NashornScriptEngine.java:528)
位于jdk。纳肖恩。应用程序编程接口。脚本编写。纳什启动了引擎。evalImpl(NashornScriptEngine.java:524)
位于jdk。纳肖恩。应用程序编程接口。脚本编写。纳什启动了引擎。eval(NashornScriptEngine.java:194)
在javax上。剧本抽象脚本引擎。eval(AbstractScriptEngine.java:264)
在com上。努贝。门户。引擎。js。JsEngine。eval(JsEngine.java:111)
在com上。努贝。门户。引擎。html。标签。HtmlTagScript。eval(HtmlTagScript.java:66)
。。。。。。。。

我添加了一些自定义代码,将所有全局对象复制到另一个地图。这是为了满足其他一些要求,我必须以“nube”的形式访问所有全局对象。我不知道这段代码是否会给频繁运行带来任何问题。请记住,我不会从上下文中删除任何对象。

public void movePublicObjects(String prefix) throws NubeException{
    Bindings b1 = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
    Map<String, Object> nubeObjects = new HashMap<String, Object>();
    for(Entry<String, Object> entry: b1.entrySet()){
        if(!entry.getKey().equals("nube")){
            nubeObjects.put(entry.getKey(), entry.getValue());
        }
    }
    b1.put("nube", nubeObjects);
    return;
}

当我将JsEngine定义为原型时,这段代码工作得很好,但是性能不好。你认为这是Nashorn中的一个bug吗?

共有2个答案

陆昊
2023-03-14

要么你破坏了持久JS上下文中的某个东西,违反了优化器的假设,要么这是一个明显的错误。堆栈跟踪显示,你的代码去调用一个方法,脚本运行时中的代码路径设法到达了一个位置,在这个位置上,函数的优化版本将应用于与其假设不匹配的参数(其参数类型过滤器),因此在这个异常中失败。无论你的JS代码如何,在一致的运行时都不应该遵循这个代码路径。

凌翔宇
2023-03-14

IIRC,Spring@Component将默认为单例作用域,因此您的JsEngine的单ScriptEngine实例将跨线程共享。

我在Nashorn开发者邮件列表上发现了一个关于线程安全的讨论,它说:

. . . Nashorn不是线程安全的。事实上,如果你评估

新的NashornScriptEngineFactory(). getParameter("THREADING")

它将返回null,这意味着“引擎实现不是线程安全的,不能用于在多个线程上并发执行脚本”——请参阅http://docs.oracle.com/javase/7/docs/api/javax/script/ScriptEngineFactory.html#getParameter(java.lang.String)

Nashorn库内部本身是线程安全的。但在单个引擎实例中执行的JavaScript程序不是线程安全的。

如果您以前尝试过,这与Rhino不同。使用Nashorn,您可能需要采取措施保护您的ScriptEngine免受并发访问,这可能解释了您观察到的不可预测的行为。

考虑设置一个池,访问线程本地的引擎实例,或者将组件范围更改为原型而不是单例。

下面是一个使用初始化脚本引擎的线程本地实例的潜在解决方案:

@Component
public class JsEngine {

    private final ThreadLocal<ScriptEngine> threadEngines =
        new ThreadLocal<ScriptEngine>() {
            @Override
            protected ScriptEngine initialValue() {
                ScriptEngine engine =
                    new ScriptEngineManager().getEngineByName("nashorn");
                for (String key: defaultEngineSource.keySet()) {
                    engine.eval(defaultEngineSource.get(key));
                }
                return engine;
            }
        };

    @PostConstruct
    public void init() throws ScriptException, IOException {
        this.cacheAllCoreEngines();
        // engine initialization moved to per-thread via ThreadLocal
    }

    private void cacheAllCoreEngines() throws IOException{
       //read all core files such as mustache, etc. 
       defaultEngineSource.put("mustache", FileUtil.readFileFromDisk("<actual path..>/mustache.js"));
    }

    public Object eval(String source) throws ScriptException{
        // .... code to handle exceptions 
        return threadEngines.get().eval(source);
    }
}

对于每个使用组件的新线程,初始化引擎(使用小胡子等代码)仍然会有开销。然而,如果您的应用程序使用线程池,那么这些相同的线程应该被重用,您不会像使用原型范围那样每次调用都要支付这笔费用。

 类似资料:
  • 在这个网站上为另一个答案编写代码时,我遇到了这样一个特点: 首先,我很困惑为什么调用对编译器来说是可以的。当没有提到任何未检查的异常类型时,它推断出的可能类型是什么? 其次,既然这是可行的,那么编译器为什么要抱怨调用呢?他们看起来很像。

  • 主要内容:1 Java8 JavaScript Nashorn的介绍,2 案例:使用终端执行,3 案例:Java代码执行JavaScript文件,4 案例:Java源代码嵌入JavaScript代码,5 案例:嵌入JavaScript表达式,6 案例:JavaScript文件的heredocs,7 案例:Java文件中设置JavaScript变量1 Java8 JavaScript Nashorn的介绍 Nashorn是JavaScript引擎。它用于在JVM(Java虚拟机)上动态执行Java

  • 问题内容: 我有以下代码: 抛出一个。我不想在这里处理此问题,但是将异常从抛出给的调用者。 问题答案: 您的代码建议您稍后以相同的方法使用异步操作的结果,因此无论如何都必须进行处理,因此一种处理方法是 在的异步处理中抛出的所有异常都将在调用时包装为一个,除了我们已经包装在一个。 当重新引发的原因时,我们可能会遇到未检查的异常,即or的子类或我们的自定义检查的异常。上面的代码通过多次捕获来处理所有这

  • 我在android版本中使用metada exttractor:2.7.0. 这个库有时会抛出这样的异常:` 这只在相同的情况下有时抛出,我不能捕捉或解决这个问题,因为是在库......

  • 我想把字符串从Main传递到Header。它成功了,但也发出了警告。我是React的初学者,所以我不知道它一定是函数的意思。 有人知道如何解决这个警告吗? 警告是: 我的代码如下: 主要的js Header.js

  • 我知道有很多类似的问题,通过阅读这些问题的答案,我得到了很大的帮助,但是我无法看到我的客户如何面对这个问题。只有一个客户面临这个问题。 我有一个列表,我正在使用比较器界面对该列表进行排序。你们中有人看到以下代码有问题吗? 这就是我调用这个函数的方式 我知道这种情况下的主要问题是传递性。然而,我不知道是什么违反了这条规则。 这如何getSampleDateTime()返回日期Fri Apr 09 1