【问题标题】:How to process large XML file (9 GB) with STAX api如何使用 STAX api 处理大型 XML 文件 (9 GB)
【发布时间】:2016-02-04 17:55:13
【问题描述】:

我在处理大文件时总是遇到堆内存问题。这里我正在处理 9 GB xml 文件。

这是我的代码。

            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            InputStream in = new FileInputStream(sourcePath);
            XMLEventReader eventReader =  inputFactory.createXMLEventReader(in);

            Map<String, Cmt> mapCmt = new ConcurrentHashMap<String, Cmt>();
            while (eventReader.hasNext()) {
                XMLEvent event = eventReader.nextEvent();
                if (event.isStartElement()) {
                //some processing and assigning value to map
                Cmt cmt = new Cmt();
                //get attributes
                cmt.setDetails(attribute.getValue());
                mapCmt.put(someKey,cmt);
                }
            }

一段时间后,我在迭代中遇到堆内存问题。 请帮我写优化代码。

注意:服务器有 3 GB 的可用堆空间。我无法增加服务器空间。 我正在使用以下参数执行 - -Xms1024m -Xmx3g

我的 xml 看起来像这样。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DatosAbonados xmlns="http://www.cnmc.es/DatosAbonados">
    <DatosAbonado Operacion="1" FechaExtraccion="2015-10-08">
        <Titular>
            <PersonaJuridica DocIdentificacionJuridica="A84619488" RazonSocial="HERMANOS ROJAS" NombreComercial="PINTURAS ROJAS"/>
        </Titular>
        <Domicilio Escalera=" " Piso=" " Puerta=" " TipoVia="AVENIDA" NombreVia="MANOTERAS" NumeroCalle="10" Portal=" " CodigoPostal="28050" Poblacion="Madrid" Provincia="28"/>
        <NumeracionAbonado>
            <Rangos NumeroDesde="211188600" NumeroHasta="211188699" ConsentimientoGuias-Consulta="1" VentaDirecta-Publicidad="1" ModoPago="1">
                <Operador RazonSocial="11888 SERVICIO CONSULTA TELEFONICA S.A." DocIdentificacionJuridica="A83519389"/>
            </Rangos>
        </NumeracionAbonado>
    </DatosAbonado>
    <DatosAbonado Operacion="1" FechaExtraccion="2015-10-08">
        <Titular>
            <PersonaJuridica DocIdentificacionJuridica="A84619489" RazonSocial="HERMANOS RUBIO" NombreComercial="RUBIO PELUQUERIAS"/>
        </Titular>
        <Domicilio Escalera=" " Piso=" " Puerta=" " TipoVia="AVENIDA" NombreVia="BURGOS" NumeroCalle="18" Portal=" " CodigoPostal="28036" Poblacion="Madrid" Provincia="28"/>
        <NumeracionAbonado>
            <Rangos NumeroDesde="211186000" NumeroHasta="211186099" ConsentimientoGuias-Consulta="1" VentaDirecta-Publicidad="1" ModoPago="1">
                <Operador RazonSocial="11888 SERVICIO CONSULTA TELEFONICA S.A." DocIdentificacionJuridica="A83519389"/>
            </Rangos>
        </NumeracionAbonado>
    </DatosAbonado>
</DatosAbonados>

我的 Cmt 课程是:

public class Cmt {
    private List<DetailInfo> details;

    public List<DetailInfo> getDetails() {
        return details;
    }
    public void setDetails(DetailInfo detail) {
        if(details == null){
            details = new ArrayList<DetailInfo>();
        }
        this.details.add(detail);
    }
}

实际上 Cmt 对象非常少,但我有 DetailInfo 对象 每个元素。如此巨大的不。 DetailInfo 对象是 已创建。

我的逻辑是这样的:

if (startElement.getName().getLocalPart().equals("DatosAbonado")) {
                    detailInfo = new DetailInfo();

                    Iterator<Attribute> attributes = startElement.getAttributes();
                    while (attributes.hasNext()) {
                        Attribute attribute = attributes.next();
                         if(attribute.getName().toString().equals("Operacion")){
                            detailInfo.setOperacion(attribute.getValue());
                        }
                    }
                }
if (event.isEndElement()) {
                EndElement endElement = event.asEndElement();
                if (endElement.getName().getLocalPart().equals("DatosAbonado")) {
                    Cmt cmt = null;
                    if(mapCmt.keySet().contains(identificador)){
                        cmt = mapCmt.get(identificador);
                    } else{
                        cmt = new Cmt();
                    }
                    cmt.setDetails(detailInfo);
                    mapCmt.put(identificador, cmt);
}
}

【问题讨论】:

  • 在这个过程中你创建了多少Cmt对象?可能太多,无法容纳您的可用内存......
  • 你没有显示全部:attribute 来自哪里?您在 mapCmt 中存储了多少个 Cmt 对象,它们有多大?此外,为每个起始元素事件创建一个 Cmt 似乎很奇怪。
  • @laune 这里我写了一些伪代码。创建的 Cmt 对象非常少,但 Cmt 对象包含为每个 创建的另一个对象的列表。
  • @wero 我在这里写了一些伪代码。创建的 Cmt 对象非常少,但 Cmt 对象包含为每个 创建的另一个对象的列表。
  • 如果您要发布有关 DatosAbonado 的确切详细信息以及您在存储的 List 元素中存储的内容,您可能会提出建议。 XML 似乎非常多余,即两个&lt;DatosAbonado&gt; 有很多共同点。这将导致内存的巨大浪费。即使是 String.intern 也可能会减少内存。

标签: java xml io stream stax


【解决方案1】:

你的问题的根源很可能是这样的:

mapCmt.put(someKey, cmt);

您正在使用大量 Cmt 大对象填充哈希映射。您需要执行以下操作之一:

  • 立即处理数据,而不是将其保存在数据结构中。
  • 将数据写入数据库以供日后查询。
  • 增加堆大小。
  • 为您的数据找出一个不太“占用内存”的表示形式。

但最后两种方法无法扩展。随着输入文件大小的增加,您将需要越来越多的内存……直到最终超出执行平台的内存容量。

【讨论】:

  • 创建的 Cmt 对象非常少,但 Cmt 对象包含为每个 创建的另一个对象的列表。请检查更新的问题。
  • 这不会实质性地改变我的诊断或可能的解决方案。
  • 我的下一个方法是您的第二点,因为我认为我别无选择。感谢您的宝贵建议。
  • @Bunty 如果您有兴趣,可以快速研究一下“ETL”和“数据仓库”。这基本上是两者结合的第一步;从源中提取数据并将其“暂存”到一种存储格式(数据库)中,使数据结构化和可访问,并且可以轻松地对其进行验证、转换,然后加载到最终目的地。它是一种使数据处理变得健壮且易于管理的策略。非常值得的资源,IMO。
【解决方案2】:

DatosAbonnado 确实是杀手。如果你有很多'm,这将导致你的应用程序阻塞。

这种方法根本无法扩展。正如 Stephan C 所指出的,您需要在 DatosAbonnado 到达时对其进行处理,而不是将它们收集在容器中。

由于这是我开发 LDX+ 代码生成器的典型场景,因此我采取了以下步骤:

此代码生成器实际上是使用 SAX,生成的代码允许您:

  • 将 complexElements 序列化为 Java 对象
  • 配置如何在运行时处理一对多关系(如您在此处所拥有的)

我在这里上传了代码:https://bitbucket.org/lolkedijkstra/ldx-samples 要查看代码,请导航到 Source 文件夹。在那里你会找到 DatosAbonnados。

这种方法确实可以很好地扩展(内存消耗持平)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多