【问题标题】:Java String Memory Leak IIJava 字符串内存泄漏 II
【发布时间】:2011-08-29 23:24:35
【问题描述】:

我之前也问过类似的问题:Java String Memory Leak

但我不知道该问什么:

这是我写的另一段代码:

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;


public class TestString {
    public static int  readLineXX(BufferedReader br) throws Exception {
            String s = br.readLine();
            if ( s == null ) return 0;
            return 1;
    }
    public static void main(String args[]) {
        while (true) {
        try {
            FileInputStream fstream = new FileInputStream("bigfile.txt");
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader  br = new BufferedReader(new InputStreamReader(in));
            while ( readLineXX (br)!= 0) {
                //System.out.print(".");
            }
            br.close();
            in.close();
            fstream.close();
        } catch (Exception e) {
        }       
    }
    }
}

由于 Java 中的字符串是不可变的,String s 是否会被垃圾回收。 我使用-verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails 选项运行了这段代码很长时间,它运行良好。

这是我的主要应用程序的小 sn-p 代码,我认为这是泄漏并导致OOMbigfile.txt1GB 附近。

【问题讨论】:

  • 如果您在主应用程序中收到 OutOfMemoryError,但在运行时却没有收到,是什么让您认为是您的应用程序的这一部分导致了错误?如果您可以在一个小程序中重现它,您将更容易诊断问题。由于您没有重现错误,我怀疑此代码与您的应用程序之间存在关键差异。
  • 我会让您从 readLineXX 返回的内容为“return s == null ? 0 : 1;”。但是,要回答这个问题,如果这没有泄漏,那么您的问题就在其他地方。我们无法推测您的代码可能有什么问题。我们会写一些 Core Java 大小的东西......

标签: java memory-leaks garbage-collection out-of-memory


【解决方案1】:

代码中的Strings 不会导致内存泄漏,因为它们在从readLineXX 返回后不会在任何地方被引用,所以所有的都会被垃圾回收。我的嫌疑人是您正在使用的流。我可以想象,对于 1GB 的输入文件,输入流会变得非常大。

对此进行测试的一种方法是将输入文件分成 2 个或更多部分,然后依次读取每个部分。如果罪魁祸首是流,那么一个一个地读取几个较小的输入文件应该不会导致内存泄漏。

【讨论】:

  • 在我的主应用程序中,我在逐行读取文件后创建一个列表,然后进行一些操作,我想我必须看看那里。我认为 s 永远不会被删除。
  • @Avinash - 所以,您要向 List 添加 1Gb 的字符串并且内存不足?
  • 如果您真的需要从 1 GB 文件中创建一个列表,您可能需要重新考虑您的设计。当然,您可以使用 -X 标志增加 java 堆大小的大小,但是您将获得一个 16 GB 的文件并回到同一条船上。无论如何,尝试增加堆大小。 hausheer.osola.com/docs/5
  • 以下是使用 -X 增加堆大小的常见错误:javahowto.blogspot.com/2006/06/…
【解决方案2】:

是的,s 将被垃圾回收。不可变不会阻止它被垃圾回收,它只是阻止它被修改。

请注意,br.readlLine() 如果行非常大,可能会使用大量内存。例如,如果您的文本文件是 1gb 且没有任何换行符,那么 br.readLine() 很有可能消耗 2gb,其中 1gb 用于读入的数据,然后 1gb 用于创建的字符串。

【讨论】:

    【解决方案3】:

    而不是

            FileInputStream fstream = new FileInputStream("bigfile.txt");
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader  br = new BufferedReader(new InputStreamReader(in));
    

    你可以使用

            BufferedReader  br = new BufferedReader(new FileReader("bigfile.txt"));
    

    导致 OOME 的唯一方法是使用非常长的行和有限的内存。您是否使用 -Xmx 选项?为什么要使用 DataInputStream - 这是二进制数据还是文本数据?

    【讨论】:

    • 是的,尽管您的代码中有一个细微的错误:您没有指定要使用的编码,因此您将获得任意解码器平台所具有的任何内容。所以你应该指定编码作为 FileReader 的参数(或者如果没有这样的构造函数,使用 FileInputStream 和 InputStreamReader)。
    • @StaxMan:正确,您应该指定编码,但这会改变原始代码的行为。
    • 是的。我假设原来的有这个错误,所以你只是指出其他修复。很公平。
    【解决方案4】:

    代码中没有明显的内存泄漏。

    Dump the heap 带有-XX:+HeapDumpOnOutOfMemoryError JVM 参数和analyse the results。 (我假设是 Java 6 Hotspot JVM;如果您使用的是另一个,可能会有类似的选项。)

    DataInputStream 是多余的;流应该在 finally 块中关闭;但我看不出这些事情会如何影响您报告的错误。

    【讨论】:

      猜你喜欢
      • 2011-08-29
      • 2013-12-03
      • 2014-04-30
      • 2013-04-03
      • 1970-01-01
      • 2011-06-12
      • 2018-12-17
      • 2011-05-04
      • 1970-01-01
      相关资源
      最近更新 更多