java – XSLT处理递归深度

首先让我说我根本不知道XSLT.我有一项任务是调查在XSLT处理期间发生的Java OutOfMemory异常的一些JVM转储.

我发现OutOfMemory发生在递归XSLT处理期间(我们使用XALAN).

我发现令人震惊的是递归是> 10万次深度调用.

在XSLT处理过程中这种深度递归可以接受的情况是什么?

请注意,线程堆栈跟踪大约300k行,并填充了此变体,直到OutOfMemory发生:

at org / apache / xalan / transformer / TransformerImpl.executeChildTemplates(字节码PC:150(编译代码))
在org / apache / xalan / templates / ElemElement.execute(字节码PC:352(编译代码))
at org / apache / xalan / transformer / TransformerImpl.executeChildTemplates(字节码PC:150(编译代码))

最佳答案
使用原始递归处理非常长的序列时会发生这种情况.

想象一下使用递归命名模板实现sum()函数:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:call-template name="sum">
   <xsl:with-param name="pSeq" select="/*/*"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="sum">
  <xsl:param name="pAccum" select="0"/>
  <xsl:param name="pSeq"/>

  <xsl:choose>
   <xsl:when test="not($pSeq)">
     <xsl:value-of select="$pAccum"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:call-template name="sum">
     <xsl:with-param name="pAccum"
          select="$pAccum+$pSeq[1]"/>
     <xsl:with-param name="pSeq"
          select="$pSeq[position() >1]"/>
    </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

当应用于以下XML文档时:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

结果是:

55

现在,假设nums有1000000(1M)个num子节点.这将是找到一百万个数字之和的合法尝试,但是大多数XSLT处理器通常在1000或1000附近的递归深度处崩溃.

解决方案:

>使用tail-recursion(一种特殊的递归,其中递归调用是模板中的最后一条指令).一些XSLT处理器识别尾递归并在内部对迭代进行优化,因此没有递归和堆栈溢出.
>使用DVC式递归(Divide and Conquer).这适用于所有XSLT处理器.最大递归深度是log2(N),并且对于大多数实际目的是可行的.例如,处理1M项的序列需要堆栈深度仅为19.

这是sum模板的DVC实现:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:call-template name="sum">
   <xsl:with-param name="pSeq" select="/*/*"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="sum">
  <xsl:param name="pSeq"/>

  <xsl:variable name="vCnt" select="count($pSeq)"/>

  <xsl:choose>
   <xsl:when test="$vCnt = 0">
     <xsl:value-of select="0"/>
   </xsl:when>
   <xsl:when test="$vCnt = 1">
     <xsl:value-of select="$pSeq[1]"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vHalf" select=
     "floor($vCnt div 2)"/>

    <xsl:variable name="vSum1">
     <xsl:call-template name="sum">
      <xsl:with-param name="pSeq" select=
      "$pSeq[not(position() > $vHalf)]"/>
     </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="vSum2">
     <xsl:call-template name="sum">
      <xsl:with-param name="pSeq" select=
      "$pSeq[position() > $vHalf]"/>
     </xsl:call-template>
    </xsl:variable>

    <xsl:value-of select="$vSum1+$vSum2"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

使用此模板查找一百万个数字的总和需要一些时间,但会产生正确的结果而不会崩溃.

转载注明原文:java – XSLT处理递归深度 - 代码日志