看看下面的代码
for(int i = 0; i < 10; i++) {
new Object();
}
由于没有对对象的引用,垃圾收集器会在每次迭代中启动吗?通常不鼓励在没有任何引用的情况下在迭代中创建对象吗?
看看下面的代码
for(int i = 0; i < 10; i++) {
new Object();
}
由于没有对对象的引用,垃圾收集器会在每次迭代中启动吗?通常不鼓励在没有任何引用的情况下在迭代中创建对象吗?
由于没有对对象的引用,垃圾收集器会在每次迭代中启动吗?
可能不是。这不像 JVM 通常是这样引用计数的。将创建少量垃圾 - 每个对象在迭代后都有资格收集,但未指定实际收集这些对象的时间。
通常不鼓励在没有任何引用的情况下在迭代中创建对象吗?
毫无目的地创建对象通常不是一个好主意。我不确定您所说的“没有任何参考”是什么意思。如果您有理由在每次迭代中创建一个新对象,并且在迭代完成后不需要它,那很好。
Java 不保证垃圾收集何时开始。它只保证 GC (我在这里解释一下):
最终,引用计数为 的超出范围的对象
0
将被垃圾回收。
即使打电话System.gc()
也不能保证任何事情。远离任何依赖于非确定性行为的代码。
正如 Jon Skeet 所指出的,大多数 Java GC 不使用引用计数。在这里阅读更多http://www.ibm.com/developerworks/java/library/i-garbage2/和这里http://www.ibm.com/developerworks/java/library/i-garbage1/(文章是关于IBM 的 VM - 其他 VM 的 GC 算法可能会有所不同)。我主要使用术语作为过度简化。
不,垃圾收集器不会在每次迭代中启动。收集器不是完全确定的 - 没有一组特定的约束导致它运行。垃圾收集器不会仅仅因为您创建了它可以收集的对象而运行。
但是,在每次迭代中创建的对象立即符合垃圾回收条件——这是两个不同的概念。
您不知道 GC 何时运行。
您在循环中在堆上创建的每个对象在超出范围时都有资格进行 GC,但不能保证每次迭代都运行 GC。
垃圾收集器在生成已满(或在某个占用率)时启动,而不是在代码块退出时启动。
关于为短期对象优化垃圾收集的评论是有效的。
此外,请注意,在这种情况下,HotSpot 的某些方面也可能会影响垃圾收集。
逃逸分析在 Java 6 中被添加到 Oracle/OpenJDK JVM 中,并且在 OpenJDK 7 的 HotSpot 编译器中默认启用:
逃逸分析是一种技术,Java 热点服务器编译器可以通过该技术分析新对象的使用范围并决定是否将其分配到 Java 堆上。
从理论上讲,这将允许编译器确定该对象只能在方法内访问,因此它可以分配在堆栈上而不是堆上。因此,它可以在每次迭代结束时从内存中释放出来,而根本不需要垃圾收集(类似于在每次迭代后显式释放 C/C++ 中的内存)。
在实践中,OpenJDK 7 中的转义分析文档说,JDK 7 编译器当前“不会用堆栈分配替换非全局转义对象的堆分配”。所以看起来堆栈分配是一种可以实现的优化(IMO),但现在似乎没有实现。
另一个优化是让编译器(或 HotSpot)检测在此示例中创建的对象从未使用过,并且其构造函数不会修改应用程序其他地方的状态/导致副作用。在这种情况下,它可以完全从编译代码中消除该语句,因此new Object()
永远不会执行。
总之,HotSpot 继续变得更加智能,并且已经针对此类情况进行了一些优化。
现在(正如其他人提到的)GC 不太可能在每次迭代后立即启动,但它可能会很快释放这些对象(伊甸园空间/年轻一代等)。
将来 HotSpot 可能会使用堆栈分配,因此确实会在每次迭代后释放内存。或者编译器或 HotSpot 可能会完全消除此特定语句。
垃圾收集器会在每次迭代中启动吗
你不知道也不应该关心,因为这些对象将在年轻一代中,并且该区域的清理速度很快(需要小房子维护)。
通常不鼓励在没有任何引用的情况下在迭代中创建对象吗?
依靠。如果是这样,即在本地范围内创建匿名对象实际上可以帮助 GC 将它们识别为符合 GC 的条件。