可以强制对象在Gen 1或Gen 2中而不是在Gen 0中进行垃圾回收吗?

Nik*_*wal 2 .net c# garbage-collection garbage

一位采访者问我一个奇怪的问题,我无法找到答案.

是否可以强制对象在Gen 1或Gen 2中而不是在Gen 0中进行垃圾回收?

xan*_*tos 5

你不能......但是你可以延长物体的寿命直到某一点.方法内部最简单的方法是:

var myObject = new MyObject();

... some code
... some other code

// The object won't be colleted until this point **at least**
GC.KeepAlive(myObject)
Run Code Online (Sandbox Code Playgroud)

或者您可以使用GCHandle:

var myObject = new MyObject();
this.Handle = GCHandle.Alloc(new object(), GCHandleType.Normal);
Run Code Online (Sandbox Code Playgroud)

(this.Handle你的对象的字段在哪里)

也许在另一种方法中:

this.Handle.Free();
Run Code Online (Sandbox Code Playgroud)

描述GCHandleType.Normal是:

您可以使用此类型来跟踪对象并防止垃圾收集器对其进行收集.当非托管客户端持有从垃圾收集器无法检测到的唯一引用到托管对象时,此枚举成员非常有用.

请注意,如果你GCHandle在a中执行此操作("this")class,则应该实现IDisposable模式以释放this.Handle.

好的...技术上你可以:

var myObject = new MyObject();
GC.Collect(0); // collects all gen0 objects... Uncollected gen0 objects become gen1
GC.KeepAlive(myObject);
Run Code Online (Sandbox Code Playgroud)

现在myObject是gen1 ......但这不会真正解决你的问题:-)


Jon*_*nna 5

是.

public class WillAlwaysBeCollectedInGen1or2
{
  ~WillAlwaysBeCollectedInGen1or2()
  {
  }
}
Run Code Online (Sandbox Code Playgroud)

因为它有一个终结器,并且没有任何代码可以调用GC.SuppressFinalize(this),所以它总是会在最终化队列中结束而不是在它首次符合收集条件时被收集,这意味着它将始终在第一代以外的一代中收集.

当然,因为我们通常希望发生相反的事情(尽快收集对象),这说明为什么除非实际需要,否则不应该定义终结者,并且GC.SuppressFinalize(this)如果对象处于最终化不存在的状态,则应始终调用做任何有用的事情(最明显的是,如果它被明确处理,但可能有其他情况).

所以,这种知识的用处是这样的; 知道什么不该做.

通过扩展,您可以强制在Gen1或Gen2中收集任意对象,方法是在这样的对象中保留对它的强引用:

public class PreventGen0Collection
{
  private object _keptAlive;
  public PreventGen0Collection(object keptAlive)
  {
    _keptAlive = keptAlive;
  }
  ~PreventGen0Collection()
  {
  }
}
Run Code Online (Sandbox Code Playgroud)

因为PreventGen0Collection如上所述,至少在第1代之前它本身被阻止收集,传递给它的构造函数的对象甚至不能在GC扫描中被收集,因此将被提升到下一代.

再次,这演示了要避免的代码; 最终可能导致不仅仅是一个被提升的对象,而是一个完整的图形.当您不需要时,切勿使用终结器,并在可能的情况下禁止终结.