【发布时间】:2012-06-12 16:30:49
【问题描述】:
我正在阅读一个非常大的文件并从每一行中提取一些文本的一小部分。然而,在手术结束时,我只剩下很少的内存可以使用了。似乎垃圾收集器在读入文件后无法释放内存。
我的问题是:有没有办法释放这个内存?或者这是一个 JVM 错误?
我创建了一个 SSCCE 来证明这一点。它读取一个 1 mb(Java 中为 2 mb,由于 16 位编码)文件,并从每行中提取一个字符(约 4000 行,因此应该约为 8 kb)。在测试结束时,仍然使用完整的 2 mb!
初始内存使用量:
Allocated: 93847.55 kb
Free: 93357.23 kb
读入文件后(在任何手动垃圾收集之前):
Allocated: 93847.55 kb
Free: 77613.45 kb (~16mb used)
这是意料之中的,因为程序正在使用大量资源来读取文件。
然后我进行垃圾收集,但并非所有内存都被释放:
Allocated: 93847.55 kb
Free: 91214.78 kb (~2 mb used! That's the entire file!)
我知道手动调用垃圾收集器并不能为您提供任何保证(在某些情况下它是懒惰的)。然而,这发生在我较大的应用程序中,其中文件几乎耗尽了所有可用内存,并导致程序的其余部分内存不足,尽管需要它。这个例子证实了我的怀疑,从文件中读取的多余数据没有被释放。
这里是生成测试的 SSCCE:
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);
}
}
【问题讨论】:
-
除非你正在做一些其他人不可能做的非常不寻常的事情,否则“jvm bug”不应该是你的第一个假设。
-
您希望 System.gc() 如何释放所有内存?您仍在使用 al 中的字符串,因此无法释放它们。
-
@PaulTomblin:我研究这个问题已经有一段时间了,但没有任何结果。而且我看不出有什么好的理由说明为什么会发生这种情况。
-
@dystroy 的回答一针见血。 substring 并没有创建一个全新的字符串,它包含对原始字符串的引用。
-
substring确实保留了原始字符数据。
标签: java performance memory garbage-collection