构建大型 js 对象(字符串)时出现 Nashorn OutOfMemoryError

Nashorn OutOfMemoryError when building large js objects (strings)

我在 1.8u60 上使用 Nashorn 创建模型对象以传回视图层 (thymeleaf)。模型对象的一部分是一个有点大的字符串(不够大,不会在普通 java 中引起任何问题),其中包含 HTML。当尝试使用 ScriptObjectMirror 方法将对象转换回 Java 时,我遇到了以下异常。更改最大堆大小似乎没有任何影响(从 900mb 更改为 1800mb,同样的错误)。我在网上找不到太多关于这个的信息,但是 Nashorn 对对象大小有什么限制吗?我现在要试用最新的 1.8 JDK。

java.lang.OutOfMemoryError: Java heap space
    at jdk.nashorn.internal.runtime.ConsString.flatten(ConsString.java:105)
    at jdk.nashorn.internal.runtime.ConsString.flattened(ConsString.java:98)
    at jdk.nashorn.internal.runtime.ConsString.toString(ConsString.java:69)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.wrap(ScriptObjectMirror.java:704)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.wrapLikeMe(ScriptObjectMirror.java:721)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.wrapLikeMe(ScriptObjectMirror.java:730)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.access0(ScriptObjectMirror.java:64)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.call(ScriptObjectMirror.java:371)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.call(ScriptObjectMirror.java:364)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.inGlobal(ScriptObjectMirror.java:859)
    at jdk.nashorn.api.scripting.ScriptObjectMirror.entrySet(ScriptObjectMirror.java:364)

...

谢谢,阿德里安

那一行是

        final char[] chars = new char[length];

看来最终字符串确实没有足够的内存。 Nashorn 使用 ConsString 作为一种通过延迟连接直到使用结果来分摊连接成本的方法(大多数 JS 引擎使用此优化,否则例如在循环中连接大量字符串将需要 O(n^2) 时间)。

这意味着您可能有许多 + 字符串运算符的结果是一次获得 "flattened" 的 ConsString 对象树。线性化串联时间的权衡是需要保留那些 ConsStrings,这将需要字符串所需内存的两倍多(超过两倍,因为 ConsString 对象自身的开销)。

解决此问题的一种方法是定期调用 str.toString()。它看似是一个空操作,但在内部它会强制扁平化连接树。尝试在某个时候将它引入您的代码,看看它是否有帮助。