JVM 堆,在使用 Saxson 9.6 HE 库进行 xslt 转换后保存内存密集型转换对象



问题摘要:在使用 saxon 9.6 HE 库将 xslt 转换为模板后向哈希图添加值时,堆分配增长到 330MB,几乎占堆(Xmx512 和 Xms32(的 70%。当更多项目被添加到购物车时,它会提示 512 标记并进入 OOM 生成 phd 和 javacore 文件。

我们尝试了什么:当我们使用 Saxon 9.9 HE 版本时,它在整个堆中节省了大约 30 MB,但总体上仍然为 300 MB

目标:

1) Goal is to reduce the memory footprint. 
2) Is there any fine tuning as per saxon libraries to reduce this huge heap for the transformed objects
3) We wouldn't want to remove those hashmaps from memory as those templates are needed for faster printing at the end of a cart transaction (like in a point of sale system) - hence we haven't used the getUnderlyingController.clearDocumentPool() in saxon;

代码详细信息 :

构造函数中的撒克逊初始化

package com.device.jpos.posprinter.receipts;
import java.util.HashMap;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import net.sf.saxon.TransformerFactoryImpl;
import com.device.jpos.posprinter.receipts.saxon.SaxonFunctions;
public class ReceiptXSLTTemplateManager
{
private HashMap<String, String>    xsltTemplates      = null;
private HashMap<String, Templates> xsltTransTemplates = null;
private TransformerFactory         transformerFact    = null;
public ReceiptXSLTTemplateManager( String xsltProcessor )
{
this.xsltProcessor = xsltProcessor;
setTransformerFactory();
xsltTemplates = new HashMap<String, String>();
xsltTransTemplates = new HashMap<String, Templates>();
// create an instance of TransformerFactory
transformerFact = javax.xml.transform.TransformerFactory.newInstance();
if ( transformerFact instanceof TransformerFactoryImpl )
{
TransformerFactoryImpl tFactoryImpl = (TransformerFactoryImpl) transformerFact;
net.sf.saxon.Configuration saxonConfig = tFactoryImpl.getConfiguration();
SaxonFunctions.register( saxonConfig );
}
}
}

xslt 的转换和添加到哈希图

public boolean setTransformer( String name )
{
if ( xsltTemplates.containsKey( name ) )
{
StringReader xsltReader = new StringReader( xsltTemplates.get( name ) );
javax.xml.transform.Source xsltSource = new javax.xml.transform.stream.StreamSource( xsltReader );
try
{
Templates transTmpl = transformerFact.newTemplates( xsltSource );
xsltTransTemplates.put( name, transTmpl );
return true;
}
catch ( TransformerConfigurationException e )
{
logger.error( String.format( "Error creating XSLT transformer for receipt type = %s.", name ) );
}
}
else
{
logger.error( String.format( "Error creating XSLT transformer for receipt type = %s.", name ) );
}
return false;
}

因此,即使 xsl 模板的大小范围为 200 KB 到 500 KB, 转换后,它们的内存大小在 5 到 15 MB 之间。我们有 45 个这样的文件,总共消耗了近 70% 的 JVM 堆。 当与使用堆内存的其他操作结合使用时,结果是来自 JVM 的内存不足错误。

从博士文件输出的内存分析器(图像链接(:

显示哈希映射条目和 s9api 转换的内存分析器

从 phd 文件输出的内存分析器(图像链接(

向下钻取的哈希图条目(图像链接(

我们的问题如下:1( 为什么磁盘上 200 KB 到 500KB 文件大小的模板在转换后会占用 5 MB 到 15 MB 的巨大内存? 2( 在通过 saxon 9.6 HE 进行哈希映射之前,可以优化模板的创建方式,或者我们应该以特定方式使用其他版本的 saxon 来克服这种内存占用。

请指教。感谢您的宝贵时间!

编译样式表的内存占用从来都不是我们认真研究或视为问题的事情 - 除了可能在生成字节码时,我们现在"按需"这样做以防止最严重的过度。重点始终是最大执行速度,这意味着创建一些相当复杂的数据结构,例如支持模板规则匹配的决策表。还保留了相当多的数据,仅用于提供良好的运行时诊断。

在过去的某个时候,我们确实努力确保实际的样式表树在编译后可以被垃圾回收,但我知道现在有对树的引用可以防止这种情况发生。我不确定这是一个多么重要的因素。

如果您运行的是 Saxon-EE,那么您可以尝试导出并重新导入已编译的样式表。这将强制断开指向编译期间仅暂时使用的数据结构的链接,这可能会节省一些内存。

此外,Saxon-EE 对模板规则进行 JIT 编译,因此,如果有许多模板规则因为您只使用大型 XML 词汇表的一小部分而从未被调用,那么这将节省内存。

如果您的 45 个样式表具有重叠的内容,那么将这些共享组件移动到单独编译的 XSLT 3.0 包中会很有用。

检查是否未在多个优先级级别导入相同的样式表模块。我过去见过这会导致严重的效率低下。

同时,我在 https://saxonica.plan.io/issues/4335 上记录了一个问题,以提醒下次有机会时查看此问题。

最新更新