什么是垃圾收集器实际收集的时间?它会在一段时间后或在一定量的内存耗尽后发生吗?还是有其他因素吗?
答案 0 :(得分:28)
当它确定是时候运行时运行。分代代垃圾收集器的一个常见策略是在分配0代内存失败时运行收集器。也就是说,每次分配一小块内存(大块通常直接放入“旧”代)时,系统会检查gen-0堆中是否有足够的可用空间,如果没有,则运行GC为释放空间以便分配成功。 然后旧数据被移动到gen-1堆,当空间用完时,GC运行一个集合,升级第二代堆中最长的数据,依此类推。所以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 theory and practice: Garbage collection and performance)。
因此,Java实际上已经有了几代人的GC。 Java 6中的新功能是Java 6u14中提供的Garbage-First垃圾收集器(G1)。根据{{3}}:默认情况下未启用。并行收集器仍然是默认的GC,是普通家庭使用的最有效的GC。 G1意味着是并发收集器的替代品。它旨在提高可预测性,并通过内存区域设计实现快速分配。
答案 1 :(得分:7)
您可以尝试这个小程序来检查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
答案 2 :(得分:5)
这在很大程度上取决于您实际使用的垃圾收集器,调整方式以及大量输入。
对于HotSpot垃圾收集器(Java附带的常见垃圾收集器)的运行及其调整方式,您可以查看this link
答案 3 :(得分:2)
这完全取决于实际的JVM以及它选择做什么,并且作为程序员基本上不在你手中。 Greybearded顽固的专家可能想要告诉他们更了解的JVM,但对于凡人而言,这应该被视为黑魔法更好地留下。
如果能够跟上程序创建和丢弃对象的速度,您应该关心的是什么。如果不是,则在全局清理发生时暂停整个程序。事实证明,响应时间非常糟糕,但现代计算机上的现代JVM很少发生。
如果您对程序中发生的事情以及何时感到好奇,那么请研究最新版本的Java 6 JDK中的“jvisualvm”工具。偷看里面非常棒。
答案 4 :(得分:1)
垃圾收集器在需要资源的情况下运行,并且通过使用System.gc()
告知何时是花费CPU进行收集的好时机而定期运行您可以通过显式地声明引用来帮助垃圾收集器,例如通过提供分配资源的对象init()
方法和显式清理这些资源并使其引用为空的cleanup()
方法。通过自己使引用归零,可以防止tye垃圾收集器必须找到需要更多路径到根目录的对象集群。
答案 5 :(得分:1)
当JVM没有必要的内存空间来运行时,垃圾收集器将运行并删除不必要的对象并为JVM分配内存。
不必要的对象是没有引用(地址)的对象。
对象有4个点符合垃圾收集条件。
空引用
垃圾收集器可以在引用变量时删除对象 对象的值被赋值为null,因为它的值
A a = new A();
a = null;
重新分配
当另一个对象被分配给一个的引用变量时 对象旧的引用对象可以被垃圾删除 集电极。
A a = new A(100);
a =new A(200);
本地范围
如果在对象符合条件的块内创建了对象 阻止垃圾收集器。
if(condition){
A a = new A();
}
分离
对象可以引用另一个对象,但必须至少有一个对象 否则,堆栈中这些对象的引用(地址)变量 所有这些对象都有资格使用垃圾收集器。
class A{
A r;
A(int i){
//something
}
}
A a1 = new A(100);
a1.r = new A(101);
a1.r.r = new A(102);
a1.r.r.r = a1;
a1 = null //all ojects are eligible to garbage collector