首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java不是垃圾回收内存

Java不是垃圾回收内存
EN

Stack Overflow用户
提问于 2012-06-08 23:32:16
回答 2查看 9.4K关注 0票数 12

我正在读取一个非常大的文件,并从每行提取一些小部分文本。然而,在操作结束时,我只剩下很少的内存可用。垃圾收集器在读取文件后似乎无法释放内存。

我的问题是:有什么方法可以释放这个内存吗?或者这是一个JVM错误?

我创建了一个SSCCE来演示这一点。它读取一个1MB(在Java中是2MB,因为采用16位编码)文件,并从每行(大约4000行,所以应该大约是8KB)提取一个字符。在测试结束时,仍然使用完整的2MB!

初始内存使用量:

代码语言:javascript
复制
Allocated: 93847.55 kb
Free: 93357.23 kb

在读取文件之后立即(在任何手动垃圾收集之前):

代码语言:javascript
复制
Allocated: 93847.55 kb
Free: 77613.45 kb (~16mb used)

这是意料之中的,因为程序正在使用大量资源来读取文件。

然而,然后我进行了垃圾收集,但并不是所有的内存都被释放:

代码语言:javascript
复制
Allocated: 93847.55 kb
Free: 91214.78 kb (~2 mb used! That's the entire file!)

我知道手动调用垃圾收集器不会给你任何保证(在某些情况下它是惰性的)。然而,这是在我的大型应用程序中发生的,其中文件几乎消耗了所有可用内存,并导致程序的其余部分耗尽内存,尽管需要它。这个例子证实了我的怀疑,即从文件中读取的多余数据没有被释放。

下面是生成测试的SSCCE:

代码语言:javascript
复制
import java.io.*;
import java.util.*;

public class Test {
    public static void main(String[] args) throws Throwable {
        Runtime rt = Runtime.getRuntime();

        double alloc = rt.totalMemory()/1000.0;
        double free = rt.freeMemory()/1000.0;

        System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);

        Scanner in = new Scanner(new File("my_file.txt"));
        ArrayList<String> al = new ArrayList<String>();

        while(in.hasNextLine()) {
            String s = in.nextLine();
            al.add(s.substring(0,1)); // extracts first 1 character
        }

        alloc = rt.totalMemory()/1000.0;
        free = rt.freeMemory()/1000.0;
        System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);

        in.close();
        System.gc();

        alloc = rt.totalMemory()/1000.0;
        free = rt.freeMemory()/1000.0;
        System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-06-08 23:35:48

在生成子字符串时,您的子字符串保留了对原始字符串的字符数组的引用(这种优化使得处理字符串的许多子字符串非常快)。因此,当您将子字符串保存在al列表中时,就是将整个文件保存在内存中。要避免这种情况,请使用以字符串为参数的构造函数创建一个新字符串。

所以基本上我建议你这样做

代码语言:javascript
复制
    while(in.hasNextLine()) {
        String s = in.nextLine();
        al.add(new String(s.substring(0,1))); // extracts first 1 character
    }

String(String)构造函数的源代码明确指出,它的用法是为了修剪“行李”:

代码语言:javascript
复制
  164       public String(String original) {
  165           int size = original.count;
  166           char[] originalValue = original.value;
  167           char[] v;
  168           if (originalValue.length > size) {
  169               // The array representing the String is bigger than the new
  170               // String itself.  Perhaps this constructor is being called
  171               // in order to trim the baggage, so make a copy of the array.
  172               int off = original.offset;
  173               v = Arrays.copyOfRange(originalValue, off, off+size);
  174           } else {
  175               // The array representing the String is the same
  176               // size as the String, so no point in making a copy.
  177               v = originalValue;
  178           }
  179           this.offset = 0;
  180           this.count = size;
  181           this.value = v;

更新:这个问题在OpenJDK 7更新6中消失了。使用最新版本的人不会有这个问题。

票数 22
EN

Stack Overflow用户

发布于 2012-06-08 23:38:19

System.gc()并不能保证JVM会进行垃圾收集--它只是给JVM一个建议,告诉它可以尝试垃圾收集。由于已经有大量内存可用,JVM可能会忽略这个建议,继续运行,直到它觉得有必要这样做为止。

在documentation http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#gc()上阅读更多信息

关于它的另一个问题可以在When does System.gc() do anything上找到

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10951812

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档