下面是一个略有不同的示例,其中包含最终的引用类型字段,而不是最终的值类型的局部变量:
public class MyClass {
public final MyOtherObject obj;
}
每次创建 MyClass 实例时,都将创建对 MyOtherObject 实例的传出引用,并且 GC 必须按照该链接查找活动对象。
JVM使用标记扫描GC算法,该算法必须检查GC“根”位置中的所有实时引用(如当前调用堆栈中的所有对象)。每个活动对象都被“标记”为活动对象,并且活动对象引用的任何对象也被标记为活动状态。
标记阶段完成后,GC 将扫描堆,为所有未标记的对象释放内存(并为剩余的活动对象压缩内存)。
此外,重要的是要认识到Java堆内存被划分为“年轻一代”和“老一代”。所有对象最初都分配给年轻一代(有时称为“托儿所”)。由于大多数对象都是短暂的,因此GC在从年轻一代中释放最近的垃圾方面更加积极。如果一个对象在年轻一代的收集周期中幸存下来,它就会被转移到老一代(有时称为“终身一代”),其处理频率较低。
所以,在我的头顶上,我要说“不,'最终'修饰符无助于GC减少其工作量”。
在我看来,在Java中优化内存管理的最佳策略是尽快消除虚假引用。您可以通过在使用完对象引用后立即将其“null”分配给它来做到这一点。
或者,更好的是,最小化每个声明范围的大小。例如,如果在 1000 行方法的开头声明一个对象,并且该对象在该方法的作用域(最后一个右大括号)结束之前保持活动状态,则该对象可能会保持活动状态,这实际上需要的时间要长得多。
如果您使用只有十几行代码的小方法,那么在该方法中声明的对象将更快地超出范围,并且GC将能够在效率更高的年轻一代中完成大部分工作。除非绝对必要,否则您不希望将对象移动到老一代。