3

我正在读取一些大型 XML 文件并将它们存储到数据库中。它大约为 800 mb。

它存储许多记录,然后终止并给出异常

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.IdentityHashMap.resize(Unknown Source)
    at java.util.IdentityHashMap.put(Unknown Source)

使用内存分析器,我创建了 .hprof 文件,其中显示:

  76,581 instances of "java.lang.String", loaded by "<system class loader>" occupy 1,04,34,45,504 (98.76%) bytes. 

Keywords
java.lang.String

我有用于检索值的 setter 和 getter。我该如何解决这个问题。任何帮助将不胜感激。

在此处输入图像描述

我已经通过JRE增加了内存。初始化_ 但问题没有解决

编辑:我正在使用 scireumOpen 来读取 XML 文件。

我使用的示例代码:

public void readD() throws Exception {

        XMLReader reader = new XMLReader();

        reader.addHandler("node", new NodeHandler() {

            @Override
            public void process(StructuredNode node) {
                try {



                    obj.setName(node
                            .queryString("name"));

                    save(obj);

                } catch (XPathExpressionException xPathExpressionException) {
                    xPathExpressionException.printStackTrace();
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }
        });

        reader.parse(new FileInputStream(
                "C:/Users/some_file.xml"));

    }

    public void save(Reader obj) {

        try {
            EntityTransaction entityTransaction = em.getTransaction();
            entityTransaction.begin();
            Entity e1=new Entity;
            e1.setName(obj.getName());

            em.persist(e1);
            entityTransaction.commit();

        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
4

8 回答 8

5

尝试使用另一个解析器进行 XML 处理。

使用 eg 处理一个 800M 的大 XML 文件DOM是不可行的,因为它占用了很多内存。

尝试在 Java 中使用SAXotStAX并立即处理解析结果,而无需尝试将完整的 XML 文件加载到内存中。

并且不要将解析结果全部保存在内存中。尽可能快地将它们写入数据库,并尽可能缩小解析结果的范围。

也许使用数据库中的中间表并对数据库内的所有数据集进行处理。

于 2013-07-11T06:35:49.503 回答
2

你的堆没有限制,不能在内存中保存这么大的 xml。尝试使用 -Xmx JRE 选项增加堆大小。

或者

尝试使用http://vtd-xml.sourceforge.net/进行更快更轻的 xml 处理。

于 2013-07-11T06:32:28.723 回答
1
  1. 最明显的答案是增加你的 JVM 内存,正如这里已经提到的,使用java -XmxNN
  2. 使用 aSAXParser而不是DOMTree(如果您还没有这样做)。这取决于您的应用程序设计,因此您必须对其进行研究,看看这是否是一种可能的策略。
  3. 检查您的代码并尝试删除所有不需要的对象,以便它们可以从 GB 中删除。这可以包括在循环内移动变量而不是将它们放在循环外,以便尽早删除引用。将未使用的元素设置为null您不再需要它们之后。

在不知道您的代码的情况下,这只是一般指导方针。

于 2013-07-11T06:43:11.843 回答
1

我的主要提示:再次检查您的 JPA 代码。应尽可能隔离。

一个想法是使用带有注释的 JAXB。IdentityHashMap(使用键==代替equals)是一种罕见的东西,可能是 JPA,可能是 XML 标签?您还可以查看使用了哪个 XML 解析器(检查工厂类,或通过 java SPI、服务提供者接口列出所有 XML 解析器提供者)。

您可以共享字符串,例如长度小于 20 的所有字符串。使用Map<String, String>.

private Map<String, String> sharedStrings = new HashMap<>();

private String shareString(String s) {
    if (s == null || s.length() > 20) {
        return s;
    }
    String t = sharedStrings.get(s);
    if (t == null) {
        t = s;
        sharedStrings.put(t, t);
    }
    return t;
}

public void setXxx(String xxx) {
    this.xxx = sharedString(xxx);
}

您可以对 bean 中的较大文本使用压缩(GZip 流)。

于 2013-07-11T07:15:37.540 回答
1

String如果你正在使用,请不要使用。用 or 替换它。另外,尝试增加内存。我猜 2048 是可以的,但如果问题仍然存在,则将其更改为 4096m 甚至尝试使用StringBuffer6000mStringBuilder

于 2013-07-15T05:50:57.060 回答
0

您可以在启动 Java 时增加堆大小:

java -Xmx8G
于 2013-07-11T06:32:39.330 回答
0

看起来您在发布之前编辑代码,或者发布不完全正确的代码。请改正。

首先,您的代码将无法编译。

其次,不要在save函数中传递Reader。创建并填写Entityprocess(StructuredNode node)并传​​递Entity而不是Reader来保存功能。

第三,正确处理函数中的异常save。如果发生异常,则回滚事务。

于 2013-07-11T07:58:18.733 回答
0

最后我解决了我的问题。以下事情有所帮助:

1.堆大小2048就足够了。

2.另一个问题是我使用的是字符串。

并且String 对象是不可变的

不可变是指存储在 String 对象中的值不能更改。然后我们想到的下一个问题是“如果 String 是不可变的,那么我如何能够随时更改对象的内容?” . 好吧,准确地说,反映您所做更改的不是同一个 String 对象。在内部创建了一个新的 String 对象来进行更改。

请参阅字符串和字符串缓冲区之间的区别,Stringbuilder

所以我删除了 JPA 实体以外的实体的 getter 和 setter。并将所有数据直接插入数据库而不将它们设置为任何对象。

3.第三个也是主要问题是JPAEntityManager

我的代码没有确保 EntityManager 在方法完成时始终关闭。就业务逻辑中发生 RuntimeException 而言,em EntityManager 保持打开状态!

所以总是关闭它,你也可以在 finally 块中将你的对象设置为空,比如

finally {
                    Obj1 = null;
                    Obj2 = null;
                    if (entityTransaction.isActive())
                        entityTransaction.rollback();
                    em.clear();
                    em.close();

                }

请参阅如何在 Web 应用程序中关闭 JPA EntityManger

为每个答案的人+1,这对我帮助很大。我没有标记任何答案,因为我想发布完整的答案。谢谢

于 2013-07-12T06:52:21.300 回答