【问题标题】:JAXB - unmarshal OutOfMemory: Java Heap SpaceJAXB - 解组 OutOfMemory:Java 堆空间
【发布时间】:2011-11-01 15:21:19
【问题描述】:

我目前正在尝试使用 JAXB 解组 XML 文件,但似乎 XML 文件太大(~500mb)而无法处理。我不断收到java.lang.OutOfMemoryError: Java heap space@

Unmarshaller um = JAXBContext.newInstance("com.sample.xml");
Export e = (Export)um.unmarhsal(new File("SAMPLE.XML"));

我猜这是因为它试图将大型 XML 文件作为对象打开,但该文件对于 java 堆空间来说太大了。

还有其他更“节省内存”的方法来解析大约 500mb 的大型 XML 文件吗?或者也许是一个可以帮助我处理大型 XML 文件的解组器属性?

这是我的 XML 的样子

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- -->
<Export xmlns="wwww.foo.com" xmlns:xsi="www.foo1.com" xsi:schemaLocation="www.foo2.com/.xsd">
<!--- --->
<Origin ID="foooo" />
<!---- ---->
<WorkSets>
   <WorkSet>
      <Work>
         .....
      <Work>
         ....
      <Work>
      .....
   </WorkSet>
   <WorkSet>
      ....
   </WorkSet>
</WorkSets>

我想在 WorkSet 级别解组,仍然能够阅读每个 WorkSet 的所有工作。

【问题讨论】:

    标签: java xml memory


    【解决方案1】:

    您的 XML 是什么样的?通常对于大型文档,我建议人们利用 StAX XMLStreamReader,以便 JAXB 可以将文档分块解组。

    input.xml

    在下面的文档中有很多person 元素的实例。我们可以使用带有 StAX XMLStreamReader 的 JAXB 来一次一个地解组相应的 Person 对象,以避免内存不足。

    <people>
       <person>
           <name>Jane Doe</name>
           <address>
               ...
           </address>
       </person>
       <person>
           <name>John Smith</name>
           <address>
               ...
           </address>
       </person>
       ....
    </people>
    

    演示

    import java.io.*;
    import javax.xml.stream.*;
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception  {
            XMLInputFactory xif = XMLInputFactory.newInstance();
            XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
            xsr.nextTag(); // Advance to statements element
    
            JAXBContext jc = JAXBContext.newInstance(Person.class);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
                Person person = (Person) unmarshaller.unmarshal(xsr);
            }
        }
    
    }
    

    人物

    我们需要在要解组的 XML 片段的本地根上添加 @XmlRootElement 注释,而不是匹配 XML 文档的根元素。

    @XmlRootElement
    public class Person {
    }
    

    【讨论】:

    • 感谢@Blaise 这真的很有帮助。
    • 我在你的最后一行出错了,需要(在你的例子中)转换(Person) unmarshaller.unmarshal(xsr);。这是正确的吗?
    • XMLStreamReader 如何区分起始元素?例如,当它出现在任何起始元素附近时,它是否会尝试创建一个新的 Person 实例?
    • @TyC - XMLStreamReader 只是让我们以深度优先的顺序访问 XML 事件。诀窍是我们需要识别我们希望 JAXB 解组的 XML 部分的起始元素状态。然后,JAXB 会将XMLStreamReader 推进到该元素的末尾。然后我们寻找下一个我们想要解组的片段。
    • @Tyc - 你需要尝试推进XMLStreamReader 才能写出东西。您可以向XMLStreamReader询问当前节点的名称,以查看您在遍历中的位置。
    【解决方案2】:

    您可以使用-Xmx 启动参数增加堆空间。

    对于大文件,SAX 处理更节省内存,因为它是事件驱动的,并且不会将整个结构加载到内存中。

    【讨论】:

      【解决方案3】:

      我一直在做很多研究,特别是关于方便地解析非常大的输入集。确实可以结合 StaX 和 JaxB 来选择性地解析 XML 片段,但这并不总是可行或可取的。如果您有兴趣阅读有关该主题的更多信息,请查看:

      http://xml2java.net/documents/XMLParserTechnologyForProcessingHugeXMLfiles.pdf

      在本文档中,我描述了一种非常直接且易于使用的替代方法。它解析任意大的输入集,同时让您以 javabeans 方式访问您的数据。

      【讨论】:

        【解决方案4】:

        使用SAXStAX。但是,如果目标是拥有文件的内存对象表示,那么您仍然需要大量内存来保存如此大文件的内容。在这种情况下,您唯一的希望是使用-Xmx1024m JVM 选项增加堆大小(将最大堆大小设置为 1024 MB)

        【讨论】:

          【解决方案5】:

          SAX,但您必须自己构建导出对象

          【讨论】:

            【解决方案6】:

            你也可以试试这个不是很好的做法 但它的工作:)谁在乎

            http://amitsavm.blogspot.in/2015/02/partially-parsing-xml-using-jaxb-by.html

            其他明智的使用 STAX 或 SAX 或 Blaise Doughan 所说的也很好,你可以说一个标准的方式,但是如果你有复杂的 XML 结构并且你不想手动注释你的类并使用 XJC 工具。

            在这种情况下,这可能会有所帮助。

            【讨论】:

            • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效
            猜你喜欢
            • 2014-11-07
            • 1970-01-01
            • 1970-01-01
            • 2015-06-19
            • 1970-01-01
            • 1970-01-01
            • 2015-04-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多