string.split中的Java outOfMemory异常

时间:2013-07-30 15:48:08

标签: java string exception

我有一个带有整数的大文本文件。文件中的每一行都有两个由空格分隔的整数。文件大小为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()方法的内容。我认为这会导致内存泄漏,但我不知道该怎么办。

7 个答案:

答案 0 :(得分:4)

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

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

P.S。只是一般的Java注释:不要写

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

你只需要

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

甚至只是直接使用tokens[0]代替s1,因为它很明显。

答案 1 :(得分:3)

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

答案 2 :(得分:2)

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

更多信息here

答案 3 :(得分:2)

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

虽然您的异常出现在Pattern.split()方法中,但实际的罪魁祸首可能是代码中的任何大内存用户,例如您正在构建的图形。看看你提供的内容,我怀疑图形数据结构存储了大量冗余数据。您可能想要研究更节省空间的图形结构。

如果您使用的是Sun JVM,请尝试使用JVM选项-XX:+ HeapDumpOnOutOfMemoryError来创建堆转储并对任何大量内存用户进行分析,并使用该分析来优化代码。有关详细信息,请参阅Using HeapDumpOnOutOfMemoryError parameter for heap dump for JBoss

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

答案 4 :(得分:0)

你有异常,因为你的堆空间已经完成。尝试使用

增加堆
 java -Xms512 -Xmx2048 (for example)

答案 5 :(得分:0)

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

答案 6 :(得分: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.

    }

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