1

我有一个包含整数的大 txt 文件。文件中的每一行都有两个由空格分隔的整数。文件大小为 63 Mb。

Pattern p = Pattern.compile("\\s");
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        String line;
        while ((line = reader.readLine()) != null) {
            String[] tokens = p.split(line);
            String s1 = new String(tokens[0]);
            String s2 = new String(tokens[1]);
            int startLabel = Integer.valueOf(s1) - 1;
            int endLabel = Integer.valueOf(s2) - 1;
            Vertex fromV = vertices.get(startLabel);
            Vertex toV = vertices.get(endLabel);
            Edge edge = new Edge(fromV, toV);
            fromV.addEdge(edge);
            toV.addEdge(edge);
            edges.add(edge);
            System.out.println("Edge from " + fromV.getLabel() + " to " + toV.getLabel());
        }

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:2694)
at java.lang.String.<init>(String.java:203)
at java.lang.String.substring(String.java:1913)
at java.lang.String.subSequence(String.java:1946)
at java.util.regex.Pattern.split(Pattern.java:1202)
at java.util.regex.Pattern.split(Pattern.java:1259)
at SCC.main(SCC.java:25)

为什么我会收到此异常?如何更改我的代码以避免它?

编辑:我已经将堆大小增加到 2048m。什么在消耗它?这也是我想知道的。

据我所知,jvm 应该为顶点列表、边集、缓冲读取器的缓冲区和一个小字符串“line”分配内存。我看不出这个 outOfMemory 是从哪里来的。

我阅读了有关 string.split() 方法的信息。我认为它会导致内存泄漏,但我不知道该怎么办。

4

7 回答 7

4

您应该首先尝试将文件减小到足够小以使其正常工作。这将使您能够评估您遇到的问题有多大。

其次,您的问题绝对与您的问题无关,String#split因为您一次只在一行上使用它。消耗你的堆的是VertexEdge实例。您必须将其重新设计为更小的占用空间,或者彻底检查您的算法,以便能够仅使用内存中的一部分图形,其余部分在磁盘上。

PS 只是一个一般的 Java 注释:不要写

String s1 = new String(tokens[0]);
String s2 = new String(tokens[1]);

您只需要

String s1 = tokens[0];
String s2 = tokens[1];

甚至直接使用tokens[0]而不是s1,因为它很清楚。

于 2013-07-30T15:58:07.813 回答
4

最简单的方法:增加堆大小:向 jvm 添加 -Xmx512m -Xms512m (甚至更多)参数

于 2013-07-30T15:52:16.633 回答
2

-Xmx使用JVM 选项增加堆内存限制。

更多信息在这里

于 2013-07-30T15:52:55.017 回答
2

您收到此异常是因为您的程序在 java 堆中存储了太多数据。

尽管您的异常出现在 Pattern.split() 方法中,但真正的罪魁祸首可能是代码中的任何大型内存用户,例如您正在构建的图形。查看您提供的内容,我怀疑图形数据结构存储了很多冗余数据。您可能想研究一种更节省空间的图结构。

如果您使用的是 Sun JVM,请尝试使用 JVM 选项 -XX:+HeapDumpOnOutOfMemoryError 创建一个堆转储并针对任何重内存用户进行分析,并使用该分析来优化您的代码。有关更多信息,请参阅使用 HeapDumpOnOutOfMemoryError 参数进行 JBoss 的堆转储

如果正如其他人所指出的那样,这对您来说工作量太大,请尝试将 JVM 堆空间增加到您的程序不再崩溃的程度。

于 2013-07-30T16:02:30.810 回答
0

当您在尝试解析内容时遇到 OOM 时,只是您使用的方法不可扩展。即使增加堆可能会暂时解决问题,但它是不可扩展的。例如,如果明天您的文件大小增加了一个数量级或数量级,那么您将回到原点。我建议尝试分段读取文件,缓存文件的 x 行,读取它,清除缓存并重新执行该过程。您可以使用 ehcache 或 guava 缓存。

于 2013-07-30T16:01:37.127 回答
0

您解析字符串的方式可能会改变。

try (Scanner scanner = new Scanner(new FileReader(filePath))) {
    while (scanner.hasNextInt()) {
        int startLabel = scanner.nextInt();
        int endLabel = scanner.nextInt();
        scanner.nextLine(); // discard the rest of the line.
        // use start and end.

    }

我怀疑内存消耗实际上是在您构建的数据结构中,而不是您如何读取数据,但这应该更明显。

于 2013-07-30T16:02:55.583 回答
-1

你有例外,因为你的堆空间已经完成。尝试增加堆

 java -Xms512 -Xmx2048 (for example)
于 2013-07-30T15:53:30.677 回答