Java 垃圾回收器 - 它何时收集?
是什么决定了垃圾回收器何时实际收集?它是在一定时间之后还是在一定量的内存用完之后发生的?还是有其他因素?
是什么决定了垃圾回收器何时实际收集?它是在一定时间之后还是在一定量的内存用完之后发生的?还是有其他因素?
当它确定是时候运行时,它将运行。分代垃圾回收器中的常见策略是在第 0 代内存分配失败时运行收集器。也就是说,每次分配一小块内存(大块通常直接放入“较旧”的几代中)时,系统都会检查 gen-0 堆中是否有足够的可用空间,如果没有,它将运行 GC 以释放空间以使分配成功。然后,旧数据被移动到第 1 代堆,当空间耗尽时,GC 会对该堆运行一个集合,将存在时间最长的数据升级到第 2 代堆,依此类推。因此,GC不只是“运行”。它可能只在gen-0堆上运行(大多数集合都会这样做),或者如果它真的必须释放大量内存(这是很少需要的),它可能会检查每一代)。
但这远非唯一的策略。并发 GC 在后台运行,在程序运行时进行清理。某些 GC 可能作为每个内存分配的一部分运行。增量收集器可能会这样做,每次分配内存时扫描几个对象。
垃圾回收器的全部意义在于,它应该只做自己的事情,而不需要用户的任何输入。所以一般来说,你不能,也不应该预测它什么时候会运行。
我相信Suns JVM不久前获得了一代GC(也许是v1.6?我已经很久没有编写Java代码了,所以不确定这一点,但我记得不久前我很惊讶,当时新版本的卖点之一是“代际GC”。尤其是因为 .NET 从第 1 天起就有了。
其他JVM当然可以自由选择他们喜欢的任何策略。
编辑:上面关于Java和代际GC的部分是不正确的。有关更多详细信息,请参阅下文:
1.0 和 1.1 虚拟机使用标记扫描收集器,它可以在垃圾回收后对堆进行分段。从 Java 1.2 开始,虚拟机切换到了分代收集器,它具有更好的碎片整理行为(请参阅 Java 理论和实践:垃圾回收和性能)。
所以Java实际上有一代GC很长一段时间。Java 6 中的新增功能是 Java 6u14 中提供的垃圾优先垃圾回收器 (G1)。根据声称在1.6.0_14中发布的文章:默认情况下不启用它。并行收集器仍然是默认的 GC,并且是常见家庭使用中效率最高的 GC。G1 旨在作为并发收集器的替代方法。它旨在提高可预测性,并通过内存区域设计实现快速分配。
您可以尝试这个小程序来检查GC的行为
public class GCTest {
final int NELEMS = 50000;
void eatMemory() {
int[] intArray = new int[NELEMS];
for (int i=0; i<NELEMS; i++) {
intArray[i] = i;
}
}
public static void main (String[] args) {
GCTest gct = new GCTest();
// Step 1: get a Runtime object
Runtime r = Runtime.getRuntime();
// Step 2: determine the current amount of free memory
long freeMem = r.freeMemory();
System.out.println("free memory before creating array: " + freeMem);
// Step 3: consume some memory
gct.eatMemory();
// Step 4: determine amount of memory left after consumption
freeMem = r.freeMemory();
System.out.println("free memory after creating array: " + freeMem);
// Step 5: run the garbage collector, then check freeMemory
r.gc();
freeMem = r.freeMemory();
System.out.println("free memory after running gc(): " + freeMem);
}
}
可能的输出 -- 在您的案例中可能有所不同
free memory before creating array: 4054912
free memory after creating array: 3852496
free memory after running gc(): 4064184