2011-05-20 68 views
7

我不是java专家。Java字符串内存泄漏

我的代码正在将文件读入String。此代码每5分钟执行一次。 文件的大小各不相同。有时候是100,有时候是1000。

我经历了一段时间之后的内存不足。

我的问题是,当我的代码超出Reading file function的范围时,Java垃圾收集字符串?

我在网上阅读很困惑。有人说它不会被删除并使用StringBuffer

// Demonstrate FileReader. 

import java.io.*; 
class FileReaderDemo { 
    public static void read(BufferedReader br) throws Exception { 
     long length = 0; 
     String s; 
     while (true) { 
      s = br.readLine(); 
      s += "abcd"; 
      if (s == null) { 
       break; 
      } 
      length += s.length(); 
      //System.out.println(s); 
     } 
     System.out.println("Read: " + (length/1024/1024) + " MB"); 
    } 

    public static void main(String args[]) throws Exception { 
     //FileReader fr = new FileReader("FileReaderDemo.java"); 
     FileReader fr = new FileReader("big_file.txt.1"); 
     BufferedReader br = new BufferedReader(fr); 
     String s; 
     read(br); 
     fr = new FileReader("big_file.txt.1"); 
     br = new BufferedReader(fr); 
     read(br); 
     fr = new FileReader("big_file.txt.1"); 
     br = new BufferedReader(fr); 
     read(br); 
     fr = new FileReader("big_file.txt.1"); 
     br = new BufferedReader(fr); 
     read(br); 
     BufferedReader in = new BufferedReader(new InputStreamReader(System. in)); in .readLine(); 
     fr.close(); 
    } 
} 
+4

这取决于。字符串是对象。使用对象时,它取决于对象是否被任何东西引用(例如方法的返回值)。你可以发布一些代码吗? – 2011-05-20 12:56:24

+2

实际上很高兴看到代码,以及你正在用你读的字符串做什么。TRy给我们一些[SSCCE](http://sscce.org/) – Boro 2011-05-20 12:58:02

+1

我看不到代码你发布可能会运行几天。 – Anonymoose 2011-05-20 13:04:36

回答

3

您发布的代码不会泄漏内存。但是,while (true)循环将永远不会终止,因为s在您测试它时不会终止null


让我们改变一下,以便为“工作”

public static void read(BufferedReader br) throws Exception { 
      long length = 0; 
      String s = ""; 
      while (true) { 
        String ss = br.readLine(); 
        if (ss == null) { 
          break; 
        } 
        s += ss; 
        length += ss.length(); 
      } 
      System.out.println("Read: " + (length/1024/1024) + " MB"); 
    } 

此代码不会造成内存泄漏是因为在方法创建的字符串都将垃圾收集的候选人时,该方法返回(如果不在之前)。

我们每次执行s += ss;时都会创建一个新字符串,其中包含s中的所有字符和ss中的字符。假设有N行包含平均L个字符,则s += ss;语句将被调用N次,将创建N个字符串,并将平均复制(N * L)^2/2个字符。


然而,有一个很好的理由让一个StringBuilder,那就是减少字符串分配和角色复制的是那张量。让我们重写方法来使用StringBuilder;即不是同步的StringBuffer的替代品。

public static void read(BufferedReader br) throws Exception { 
      long length = 0; 
      StringBuilder sb = new StringBuilder(sb); 
      while (true) { 
        String ss = br.readLine(); 
        if (ss == null) { 
          break; 
        } 
        sb.append(ss); 
        length += ss.length(); 
      } 
      System.out.println("Read: " + (length/1024/1024) + " MB"); 
    } 

这个版本将重新分配的StringBuilder的内部字符数组最多log2(N)次,最多2 * N * L字符复制


总结 - 使用StringBuilder是一个好主意,但不是因为内存泄漏。如果您有内存泄漏,它不在原始示例代码或固定版本中。

6

你好,我不是Java专家。

每个人都有他们可以学习的东西。

我的代码正在将文件读入字符串中,此代码每5分钟执行一次。现在有时文件大小为100行,有时为1000行。

听起来不是很大或很多。不应该是一个问题。

我经历了一段时间之后内存不足。

你应该能够得到一个堆转储,看看你的内存不足以及为什么。

问题我有,当我的代码超出了阅读文件函数的范围。 Java垃圾收集字符串。

它可以通过强参考不可达时收集。

我很阅读互联网上的混淆也有人说,它不会删除,并用StringBuffer

听起来像是你来到了正确的地方。我从来没有听说过。

5

您的read方法将永不终止。一旦到达文件末尾,您只需继续将字符串"nullabcd"永久添加到s即可。

编辑:忘记,s每次重新分配。不过,我看不到你的方法可以终止。

+0

是啊,我有一种强烈的感觉是,发布的代码不是实际的代码有问题。 – Anonymoose 2011-05-20 13:14:12

2

更改下面的程序以消耗更少的内存。内存消耗的一个巨大来源是由于你的重复字符串连接s += "abcd"; - 避免这种情况,你可能会减半你的内存消耗(未测试 - 如果你想知道自己的配置文件)。

public static void read(BufferedReader br) throws Exception { 

    long length = 0; 
    //String s; <--- change to the line below 
    StringBuilder sb = new StringBuilder(); 
    while (true) { 
     String s = br.readLine(); 
     if (s == null) { 
      break; 
     } 
     //s += "abcd"; <--- change to the line below 
     sb.append(s).append("abcd"); 
     length += s.length(); 
     //System.out.println(s); 
    } 
    System.out.println("Read: " + (length/1024/1024) + " MB"); 
} 
1

正如别人指出的那样,这段代码永远不会终止。它看起来像你发布的代码不是你有问题的原始代码。

很难诊断但没有看到实际的代码,但是字符串肯定会被垃圾回收,一旦它们没有被其他代码引用。

疯狂猜测:一旦你完成它们,你是否在读者和InputStreams上打电话close()?如果没有,这可能是你内存不足错误的原因。