我正在尝试在 java 中解析一个最大为 500 mb 的 XML 文件。我尝试使用 SAX,但它给了我这个错误 java.lang.OutOfMemoryError: Java heap space at com.sun.org.apache.xerces.internal.util.XMLStringBuffer.append(Unknown Source) 你能帮帮我吗?非常感谢。PS 较小的 XML 文件可以正常工作
7 回答
很可能您没有正确使用 SAX,或者您的应用程序不适合流处理。
SAX 的重点是避免将整个 XML 结构保存在内存中,但这只有在您可以在不保留太多上下文的情况下以小块处理 XML 并且处理的结果比处理后的 XML 小得多的情况下才有可能(这样它也不会使用太多内存)或者本身可以传递给接收者或连续写入磁盘。
编辑:您也有可能只是发生了内存泄漏,即您持有不再需要的数据,从而阻止它被垃圾收集。如果您使用任何列表、映射或集合来处理 XML,请确保在处理一个 XML 块时添加到它们的任何内容在开始下一个块之前都已删除。
尝试使用 XML 的 Streaming API(java6 中的新功能)
http://www.javabeat.net/articles/14-java-60-features-part-2-pluggable-annotation-proce-2.html
您可以尝试通过指定例如来增加 Java 堆大小
java -Xmx1024M MyClass
在命令行上(或任何适合您文档大小的值)。
StAX for Java 6 之前的版本:http: //stax.codehaus.org/
假设您有以下 XML 结构:
<?xml version="1.0"?>
<list>
<item>
<name>Alpha</name>
<age>10</age>
</item>
<item>
<name>Beta</name>
<age>20</age>
</item>
<!-- many many items -->
</list>
你想得到所有的 <item>
public class Item
{
String name;
String age;
}
您的 SAX 处理程序将如下所示
public class MyHandler extends DefaultHandler
{
Item current=null;
StringBuilder content=null;
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
{
if(name.equals("item"))
{
current= new Item();
}
else if(name.equals("name") || name.equals("age"))
{
content= new StringBuilder();
}
}
@Override
public void endElement(String uri, String localName, String name) throws SAXException
{
if(name.equals("item"))
{
//DO SOMETHING WITH current
System.out.println(current);
current=null;
}
else if(name.equals("name"))
{
current.name= content.toString();
}
else if(name.equals("age"))
{
current.age= content.toString();
}
content=null;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException
{
if(content!=null)
{
content.append(ch,start,length);
}
}
}
如您所见,content
仅在age
andname
标记之间被记忆。
您可能想查看 ScaleDOM,它允许解析非常大的 XML 文件:https ://github.com/whummer/scaleDOM
由于 XML 节点的延迟加载,ScaleDOM 占用的内存很小。它只将 XML 文档的一部分保存在内存中,并在必要时从源文件中重新加载节点。
看看 Apache Digester。