C#-终结器中回收资源的弊端

问题描述

正如标题所述:回收包含对象的终结器中的资源(如大型数组)是否有不利之处?到目前为止,它运行良好,但是由于终结器可能有点时髦且难以调试,因此我决定在这里询问。

用例是一个多边形类,仅表示点列表。我的应用程序大量使用了几千个点的大型多边形-分配这些数组通常并不便宜。不幸的是,处理模式是不可能的,因为它们被传递了,并且几乎只有GC知道何时没有其他对象引用它们。

这就是我的实现方式(数组的长度始终为2^sizeLog2,只有带有sizeLog2 >= MIN_SIZE_LOG_2的数组被回收):

构造函数

public polygon(int capacity = 1)
{
  //fast method for getting the ceiling of the logarithm of an integer
  int sizeLog2 = UIM.CeilingLog2((uint)capacity); 
  //supress finalize when the array should not be recycled
  if(sizeLog2 < TWO_POW_MIN_SIZE) GC.SuppressFinalize(this);
  Points = Create(sizeLog2);
}

创建大小为2 ^ sizeLog2的数组:

private static Pnt[] Create(int sizeLog2)
{
  if (sizeLog2 >= TWO_POW_MIN_SIZE)
  {
    if (/*try to get an item from recycle queue*/) return result;
    Pnt[] points = new Pnt[1 << sizeLog2];
    //keep array alive so that it won't get collected by GC when the polygon is
    GC.KeepAlive(points);
    return points;
  }
  return new Pnt[1 << sizeLog2];
}

增加容量方法中,这是用于重新注册要最终确定的多边形的行,如果到现在为止都无法确定最终确定(容量只能增加2倍):

if (newSizeLog2 == MIN_SIZE_LOG_2) GC.ReRegisterForFinalize(this);

这是析构函数

~polygon()
{
  if (Points != null) Recycle(Points); //enqueues the array in a ConcurrentQueue
  Points = null;
}

是的,我确实知道这并不完全是一种出色的编程风格,但这对性能至关重要,因此我真的不能只让GC来完成所有工作,因为这样我最终要占用数百MB几秒钟之内就会出现大对象堆。

此外,多边形同时由不同的线程处理。

解决方法

简短的答案是肯定的。大型数组不是终结器要回收的资源类型。终结器应用于外部资源,并且在极少见的情况下应用于应用程序状态。

我建议this的文章和/或这篇one,以更好地理解敲定的陷阱。

您所描述的问题的症结在于:“我真的不能只让GC来完成所有工作,因为那样的话,我在几秒钟内就在大型对象堆上获得了数百MB的内存。”

但是在GC“完成所有工作”之前,永远不会调用终结器,因此您必须不完全了解是什么导致应用程序的内存压力。

这听起来确实像您的应用程序总体设计存在问题,您很难找到谁拥有多边形和/或Pnt的原因。结果是,当您不希望有多边形/ Pnt时,可以在某处引用它们。使用良好的探查器来查找发生这种情况的位置并解决此总体设计问题,而不要尝试使用终结处理。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...