XSLT 处理递归深度
首先我要声明我对 XSLT 一无所知。我接到的任务是调查 XSLT 处理期间发生的 Java OutOfMemory 异常的一些 JVM 转储。
我发现在递归 XSLT 处理期间发生了 OutOfMemory(我们使用 XALAN)。
令我震惊的是,递归调用深度超过 100 000 次。
在什么情况下,XSLT 处理过程中如此深度的递归是可以接受的?
请注意,线程堆栈跟踪大约有 300k 行长,并充满了这种情况的变体,直到发生 OutOfMemory:
at org/apache/xalan/transformer/TransformerImpl.executeChildTemplates(Bytecode PC:150(Compiled Code))
在 org/apache/xalan/templates/ElemElement.execute(Bytecode PC:352(编译代码))
在 org/apache/xalan/transformer/TransformerImpl.executeChildTemplates(字节码 PC:150(编译代码))
First of let me state that I have no clue of XSLT at all. I was given a task to investigate some JVM dumps of a Java OutOfMemory exception that occurred during XSLT processing.
I have found that the OutOfMemory occurred during recursive XSLT processing (we use XALAN).
What I found shocking was that the recursion was >100 000 calls deep.
What are the circumstances under which can a recursion this deep during XSLT processing be acceptable?
Note that the thread stack trace is about 300k lines long and filled with a variation of this until the moment the OutOfMemory occurs:
at org/apache/xalan/transformer/TransformerImpl.executeChildTemplates(Bytecode PC:150(Compiled Code))
at org/apache/xalan/templates/ElemElement.execute(Bytecode PC:352(Compiled Code))
at org/apache/xalan/transformer/TransformerImpl.executeChildTemplates(Bytecode PC:150(Compiled Code))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当使用原始递归处理很长的序列时,可能会发生这种情况。
想象一下使用递归命名模板实现
sum()
函数:当应用于以下 XML 文档时:
结果是:
现在,想象一下
nums
有 1000000 (1M)num
个子级。这将是求一百万个数字之和的合法尝试,但是大多数 XSLT 处理器通常会在递归深度为 1000 或大约 1000 时崩溃。解决方案:
使用尾递归 (一种特殊的递归,其中递归调用是模板中的最后一条指令)。一些 XSLT 处理器识别尾递归并在内部对其进行优化以进行迭代,因此没有递归,也没有堆栈溢出。
使用 DVC 式递归(分而治之)。这适用于所有 XSLT 处理器。最大递归深度为 log2(N),对于大多数实际用途来说是可行的。例如,处理 1M 项的序列只需要 19 的堆栈深度。
以下是求和模板的 DVC 实现:
使用此模板求一百万个数字的总和需要一些时间,但会产生正确的结果而不会崩溃。
This can happen when processing a very long sequence with primitive recursion.
Imagine implementing the
sum()
function with a recursive named template:when applied on the following XML document:
the result is:
Now, imagine that
nums
has 1000000 (1M)num
children. This would be a legitimate attempt to find the sum of one million numbers, however most XSLT processors typically crash at a recursion depth at or around 1000.The solution:
Use tail-recursion (a special kind of recursion where the recursive call is the last instruction in the template). Some XSLT processors recognize tail recursion and optimize it internally to iteration, so there is no recursion and no stack overflow.
Use DVC-style recursion (Divide and Conquer). This works with all XSLT processors. The maximum recursion depth is log2(N) and is feasible for most practical purposes. For example, processing a sequence of 1M items requires a stack depth of only 19.
Here is a DVC implementation of the sum template:
Using this template to find the sum of one million numbers takes some time, but produces the correct result without crashing.
这很可能是 XSLT 中的错误导致无限递归(其中“无限”定义为“直到内存耗尽”)。考虑以下模板:
文档中唯一的
template
与根元素匹配,然后对自身调用apply-templates
,这将启动一个永远不会终止的进程。It's most likely a bug in the XSLT that's resulting in infinite recursion (where "infinite" is defined as "right up until you run out of memory"). Consider the following template:
The only
template
in the document matches the root element and then callsapply-templates
on itself, which starts a process that will never terminate.