获取Java OutOfMemoryError:Java堆空间错误,我可以';t调试

获取Java OutOfMemoryError:Java堆空间错误,我可以';t调试,java,debugging,stringbuilder,out-of-memory,Java,Debugging,Stringbuilder,Out Of Memory,我正在努力找出导致OutofMemory错误的原因。让更多的内存可用不是解决方案,因为我的系统没有足够的内存。相反,我必须找到一种重新编写代码的方法 我简化了代码,试图隔离错误。请看以下内容: File[] files = new File(args[0]).listFiles(); int filecnt = 0; LinkedList<String> urls = new LinkedList<String>(); for (File f : files) {

我正在努力找出导致OutofMemory错误的原因。让更多的内存可用不是解决方案,因为我的系统没有足够的内存。相反,我必须找到一种重新编写代码的方法

我简化了代码,试图隔离错误。请看以下内容:

File[] files = new File(args[0]).listFiles();

int filecnt = 0;

LinkedList<String> urls = new LinkedList<String>();

for (File f : files) {
    if (filecnt > 10) {
        System.exit(1);
    }

    System.out.println("Doing File " + filecnt + " of " + files.length + " :" +                f.getName());

    filecnt++;
    FileReader inputStream = null;
    StringBuilder builder = new StringBuilder();

    try {
        inputStream = new FileReader(f);
        int c;
        char d;

        while ((c = inputStream.read()) != -1) {
            d = (char)c;
            builder.append(d);
        }
    }

    finally {
        if (inputStream != null) {
            inputStream.close();
        }
    }   

    inputStream.close();

    String mystring = builder.toString();
    String temp[] = mystring.split("\\|NEWandrewLINE\\|");

    for (String s : temp) {
        String temp2[] = s.split("\\|NEWandrewTAB\\|");
        if (temp2.length == 22) { 
            urls.add(temp2[7].trim());
        }
    }
}
其中主线86与线路生成器有关。附加(d);在这个例子中


但我不明白的是,如果我注释掉行url.add(temp2[7].trim());我没有得到任何错误。因此,该错误似乎是由linkedlist“URL”过度填充引起的。但是为什么报告的错误与StringBuilder有关呢?

如果linkedlist占用了您的内存,那么以后分配内存的每个命令都可能会失败,并出现OOM错误。这看起来像是您的问题。

每次添加字符串时,链表都会占用更多内存。这意味着您可能没有足够的内存来构建StringBuilder


避免此问题的方法是将结果写入文件而不是列表,因为您似乎没有足够的内存将列表保存在内存中。

您正在将文件读取到内存中。至少有一个文件太大,无法放入默认JVM堆。您可以允许它在
java
之后的命令行上使用类似于
-Xmx1g
的arg来使用更多内存

顺便说一句,一次读取一个字符的文件是非常低效的

因为这是

  • 内存不足而不是堆不足
  • 你有很多小的临时对象
  • 我建议您给JVM一个适合RAM的最大堆大小限制


    为了使用更少的内存,我会使用一个缓冲读取器来拉入整行并保存临时对象创建。

    简单的答案是:不应该将文本文件中的所有URL加载到内存中。您这样做肯定是因为您希望在下一步中处理它们。因此,不要将它们添加到内存中的列表中,而是执行下一步(可能存储在数据库中或检查是否可以访问)并忘记该URL。

    您有多少URL?看起来你只是储存了比你能处理的更多的东西

    就我所见,链表是唯一一个没有在循环中定义范围的对象,因此无法收集

    对于OOM错误,抛出到哪里并不重要


    要正确地检查这一点,请使用探查器(查看JVisualVM以获得免费的探查器,您可能已经拥有了它)。您将看到堆中有哪些对象。您还可以让JVM在崩溃时将其内存转储到文件中,然后使用visualvm分析该文件。你应该看到一件事是抓住你所有的记忆。我怀疑是所有的URL。

    与其尝试拆分字符串(这基本上是基于拆分创建一个子字符串数组),不如每次使用slpit时使用两倍以上的内存,您应该尝试对开始和结束模式进行基于正则表达式的匹配,逐个提取各个子字符串,然后从中提取URL

    另外,如果您的文件很大,我建议您甚至不要一次将所有文件加载到内存中。。。将其内容流式传输到一个缓冲区(可管理的大小),并在此基础上使用基于模式的搜索(并在处理文件内容时不断删除/添加更多缓冲区)


    该实现将使程序速度减慢一点,但占用的内存量会大大减少。

    代码中的一个主要问题是将整个文件读入字符串生成器,然后将其转换为字符串,然后将其拆分为较小的部分。因此,如果文件大小很大,您将遇到麻烦。正如其他人建议的那样,逐行处理文件,这样会节省大量内存


    此外,在处理每个文件后,您还应该检查列表的大小。如果大小非常大,您可能需要使用不同的方法或通过-Xmx选项增加进程的内存。

    这里已经有几位专家,因此,我将简要介绍这些问题:

  • 字符串生成器的不当使用:
  • StringBuilder=新的StringBuilder()

    当您一次处理少量数据时,请记住垃圾收集器

    相反,我建议您一次读取文件(文本文件)1行,处理该行,然后继续,不要仅仅为了获取字符串而创建StringBuilder的巨大内存球

    您的文本文件的引擎大小为1GB,您就完成了

  • 在读取文件时添加实际进程(如第#1项)

  • 您不需要再次关闭InputStream,finally块中的代码已经足够好了


  • 注意

    尝试替换
    url.add(temp2[7].trim())url.add的code>(新字符串(temp2[7].trim())


    我认为您的问题在于,您实际上存储的是整个文件内容,而不仅仅是URL列表中提取的URL字段,尽管这并不明显。这实际上是String类的一个特定于实现的问题,但通常String#split和String#trim返回新的String对象,这些对象包含与原始字符串相同的内部字符数组,并且只在偏移量和长度字段上有所不同。使用
    新字符串(String)
    构造函数可确保只保留原始数据的相关部分。

    是否有任何理由每次读取一个字节的文件?特别是因为你说它们是文本文件,所以以较大的块(例如逐行或诸如此类的块)进行读取可能会更有效率。此外,出于好奇,你使用的堆大小是多少,这些文件有多大?伊万托德:我一次读取一个字节是有原因的:)但我现在记不起来了,现在不需要了。viruzzo:默认堆大小(不确定是什么)
    Doing File 0 of 973 :results1322453406319.txt
    Doing File 1 of 973 :results1322464193519.txt
    Doing File 2 of 973 :results1322337493419.txt
    Doing File 3 of 973 :results1322347332053.txt
    Doing File 4 of 973 :results1322330379488.txt
    Doing File 5 of 973 :results1322369464720.txt
    Doing File 6 of 973 :results1322379574296.txt
    Doing File 7 of 973 :results1322346981999.txt
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:2882)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:572)
    at java.lang.StringBuilder.append(StringBuilder.java:203)
    at Twitter.main(Twitter.java:86)
    
    try {
        inputStream = new FileReader(f);
        int c;
        char d;
    
        while ((c = inputStream.read()) != -1) {
            d = (char)c;
            builder.append(d);
        }
    }