在.NET中,您可以设置对象的GC生成,还是以其他方式表明该对象将超出范围?

The*_*tor 3 .net garbage-collection

.NET的垃圾收集器是一种世代的垃圾收集器,其依据是大多数已分配对象都是短暂的。这样,对象从“第0代”开始,可以很容易地收集它们(并且经常如此),而寿命更长的对象则被提升为“第1代”和“第2代”,在此它们会被更全面的扫描所触及。当方法将一个对象分配为其状态的一部分时,通常会创建第0代对象,然后在该对象退出时让该对象超出范围。

但是,有时那些将对象分配为状态的方法会运行很长时间。也许他们阻塞了I / O调用,或者托管了带有一堆线程的合并排序或数据库查询。在此期间发生的垃圾回收将在方法执行期间将这些状态对象提升为第1代和第2代。尽管这样做可以防止它们进入快速垃圾回收扫描,但一旦该长时间运行的方法结束,我将不再需要这些状态对象,也不再有被调用者持有对它们的任何引用。

现在,能够将其告知垃圾收集器将是很好的。可以说,这个对象已经存在很长时间了,但是可以很快将其丢弃,并且垃圾回收器可以通过将对象移到gen 0堆中进行响应。然后,下次需要执行垃圾回收时(例如下次调用此长时间运行的方法),可以使用更快的gen 0扫描而不是完整的阻塞扫描来回收必需的内存,从而减少了垃圾收集器性能影响的不可预测性。

其他一些信息:

  • 现在,我的团队通过将这些“本地状态”对象存储在通过,refin参数传递的巨大的可变结构中,减轻了这种不可预测的性能问题。据我了解,.NET框架对可变结构之外的一些结构(字节数很少(官方消息说16,尽管我听说过24))以及可变结构的优化均很差,但我无法与以下事实进行争论:这些结构具有自动作用域,并且在方法返回时一定会释放它们-除非,当然,除非我们可以向GC发出信号,表明某个类即将退出。
  • 如果这是一个建议API,而不是命令,我将非常高兴,因为最终GC必须做它必须做的事情。也许,如果事实证明该对象仍然在某处具有引用,则最终将导致某种性能损失。
  • 对于.NET以外的垃圾收集框架中存在的此功能,我也乐于回答。

Kon*_*osa 6

您问题的简单答案是-不,没有这样的API。大多数现代GC的设计都考虑到了大多数物体年轻或寿命长的“世代假设”。您所描述的通常被称为“中年危机”-寿命足够长的物体可以升级为最老的一代,但很快就不再需要了。显然,这对于当前的GC并不是最佳的。避免这种情况的常见方法是:减少分配(被提升的机会较少,因此我们经常留在“年轻的”部分)或重用对象(因此我们进入“长期存在”的部分)。

通过大量重用结构和引用,您采取了第一种方法。您可能还会想到通过池(例如)ArrayPool<T>或其他池来重用对象。

关于这些问题,您建议使用以下API:

“垃圾收集器可以通过将对象移动到第0代堆来做出响应”

尽管它涉及很多可能的实现细节,但是很难想象有效地实现它。许多GC,包括.NET在内,都尽量不复制/移动内存。世代只是逻辑内存区域,边界来回移动。因此,“将对象移动到gen0”将需要复制它(并且可能复制所有保留的图以使其变得明智),这将带来比潜在收益大得多的开销。更糟糕的是,由于我们没有实现并发压缩(通常是在使用对象时移动对象),因此目前只能在GC期间完成。

“据我所知,.NET框架对可变结构之外的一些结构(字节数很少(官方消息说是16,尽管我听说过24))的结构进行了优化,效果不佳”

似乎有点虚假。.NET运行时对结构用法进行了很大的优化,如果您绕过它们ref,那是我能想到的最有效的方法之一。仅注意使用参数时可能遇到的防御性复制陷阱in