tsk*_*zzy 12 java memory performance garbage-collection
我正在读取一个非常大的文件,并从每一行中提取一小部分文本.但是在操作结束时,我的内存很少.在读取文件后,垃圾收集器似乎无法释放内存.
我的问题是:有没有办法释放这段记忆?或者这是一个JVM错误?
我创建了一个SSCCE来证明这一点.它读取1 mb(由于16位编码而在Java中为2 mb)文件并从每行中提取一个字符(~4000行,因此应该是大约8 kb).在测试结束时,仍然使用了完整的2 mb!
最初的内存使用情况:
Allocated: 93847.55 kb
Free: 93357.23 kb
Run Code Online (Sandbox Code Playgroud)
读取文件后立即(在任何手动垃圾收集之前):
Allocated: 93847.55 kb
Free: 77613.45 kb (~16mb used)
Run Code Online (Sandbox Code Playgroud)
这是预料之中的,因为程序正在使用大量资源来读取文件.
然而,我垃圾收集,但不是所有的内存都被释放:
Allocated: 93847.55 kb
Free: 91214.78 kb (~2 mb used! That's the entire file!)
Run Code Online (Sandbox Code Playgroud)
我知道手动调用垃圾收集器不会给你任何保证(在某些情况下它是懒惰的).然而,这发生在我的大型应用程序中,其中文件几乎占用了所有可用内存,并且导致程序的其余部分尽管需要它而耗尽内存.这个例子证实了我怀疑从文件中读取的多余数据没有被释放.
以下是生成测试的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);
}
}
Run Code Online (Sandbox Code Playgroud)
Den*_*ret 22
在创建子字符串时,您的子字符串会保留对原始字符串的char数组的引用(此优化可以非常快速地处理字符串的许多子字符串).因此,当您将子字符串保留在al列表中时,您将整个文件保留在内存中.要避免这种情况,请使用以字符串作为参数的构造函数创建一个新String.
所以基本上我建议你这样做
while(in.hasNextLine()) {
String s = in.nextLine();
al.add(new String(s.substring(0,1))); // extracts first 1 character
}
Run Code Online (Sandbox Code Playgroud)
String(String)构造函数的源代码明确指出它的用法是修剪"行李":
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;
Run Code Online (Sandbox Code Playgroud)
更新:这个问题在OpenJDK 7,Update 6中消失了.拥有更新版本的人没有问题.
确保不再保留您不再需要的参考文献.
你仍然有al和in.的引用.
al = null; in = null;在调用垃圾收集器之前尝试添加.
此外,您需要了解如何substring实施.substring保留原始字符串,并对同一个char[]数组使用不同的偏移量和长度.
al.add(new String(s.substring(0,1)));
Run Code Online (Sandbox Code Playgroud)
不确定是否有更优雅的方式复制子字符串.也许s.getChars()对你更有用.
从Java 8中,子确实现在复制的字符.您可以验证构造函数调用自己Arrays.copyOfRange.
| 归档时间: |
|
| 查看次数: |
8741 次 |
| 最近记录: |