关于代码如何影响GC过程的一些问题

时间:2010-10-30 13:18:10

标签: memory-management garbage-collection

1)添加Dispose()或finalize方法是否会很快影响对象的GC?我问这是因为我的经理写了一些代码,添加了一个finalize()方法,希望它能够用GC。

但我认为,如果对象必须首先进行“标记”以进行收集,如果它符合某些标准。

2)写“o = null”(其中o是ref类型)对使对象更快地进行GC会有什么影响吗?在C ++或Java / C#中。

由于

2 个答案:

答案 0 :(得分:2)

注意,这个答案是关于.NET的。我根本不知道Java是如何做到这一点的。

让我们从添加终结器开始,或者更确切地说,当收集没有的对象时会发生什么。

当GC运行并发现没有带根引用的对象时,它们会被收集。

但是,如果您向该类添加终结器,当GC发现该对象符合收集条件时,它将被放置在列表中,因为它具有终结器。

除了普通的GC之外,此列表也会被处理,并且由于该对象现在具有带根引用(该列表),因此它暂时不符合收集条件。通过迭代并调用所有终结器来处理列表。这里有很多细节,我正在掩饰。

一旦调用了对象的终结器,就会从列表中删除该对象。有时候,当GC再次发现该对象时,尽管它仍然在其类型中具有终结器方法,但该对象已被标记,以便终结器不再需要运行,并且现在收集对象,就好像它没有终结者将开始。

因此,实际上,添加终结器不会更快地收集对象,而是实际上会在以后收集对象。

使对象符合收集条件的唯一方法是删除对它的所有有根引用。

调用Dispose在这方面也没有意义。 Dispose只是一个方法调用,调用它并不会将对象标记为有资格进行收集。如果在调用Dispose之后,您仍然对该对象进行了root访问,它将被保留在内存中而不会被收集。

但是,如果您的类具有Dispose方法和终结器,则Dispose方法通常会从最终化中取消注册该对象。基本上你说“Dispose现在已经处理了终结器会做的所有事情,因此调用终结器已经不再有任何意义了”。如果这样做,调用Dispose方法,然后删除对象的所有实时引用,将使它既适合收集,也跳过完成步骤。

关于你的第二个问题。

如上所述,您需要删除对该对象的所有有根引用。根提法引用是一个引用,可以追溯到程序持续时间内的某些内容,无论是静态字段,静态调用堆栈上的局部变量等等。一旦所有这些引用消失,该对象就有资格进行收集。

但是.NET中的GC非常具有攻击性。 JITter将在代码旁边存储信息,告知GC使用局部变量的方法的哪些部分,以及如果不再使用变量,例如对于方法的最后部分,即使变量仍引用对象,变量被认为是不必要的,因此可以收集对象。

例如,在这里:

public void Test()
{
    object o = new object();
    // do something else
}

在这种情况下,只要方法中不再使用o变量,在“执行其他操作”代码期间,就可以收集其中的对象。

JITter将检测程序在调试器中运行的时间,然后人为地将所有变量的生命周期延长到其作用域的末尾,以便您可以检查变量,即使它们在技术上不被视为“实时”任何变量更多。但是当没有在调试器中运行时,上面的o没有明确地为空而没有任何意义,仍然可以收集该对象。

现在,如果o是一个静态字段,它比一个方法调用存活的时间长得多,那么肯定将它显式设置为null当然会有所帮助,因为现在你要删除对该对象的有根引用

此外,如果稍后在方法中重用变量,则可以通过将当前对象设置为null来帮助使当前对象符合收集条件。 ( note 我对此并不完全确定,也许JITter可以看到不需要当前值,因此可以收集它,因为稍后你会覆盖内容)

总结一下:

  • 除非您需要,否则不要添加终结器
  • 调用Dispose与对象有多久可以收集资格
  • 无关
  • 要将某个对象“标记”为有资格进行收集,请删除对它的所有有根引用
  • 您可以通过将变量和字段显式设置为null来帮助进行收集,但对于方法不再使用的局部变量,可能没有必要(但不会受到伤害)

答案 1 :(得分:0)

爪哇:

1)否,当垃圾收集器确定对象是GC能够时调用finalize() - 它与GC发生时无关,或者对象是否在给定的运行中进行GC编辑。事实上,实现finalize() 延迟一个对象被GCed - 第一个GC传递确定该对象是GC能够的(即finalize()应该被调用)并且它不是'直到第二次传递对象被释放 - 假设终结器没有创建对象的新引用!

2)保留引用将延迟GC,因此如果仍然可以访问持有引用的对象,则破坏引用可以允许更早地对象进行GC。但是,不能保证 对象可以更早地进行GC。