http://www.blogcn.com/user8/flier_lu/index.html?id=1452116&run=.0CDCD50
Brad Abrams在其blog中发表了一篇介绍Whidbey 中对 GC 的功能增强的文章Teaching an old dog new tricks: GC fun in Whidbey。其中提到两种对Unmanaged Resource的管理的GC增强。Justin Rogers则在其blog中回应前文,提出了一些很有趣的观点The new face of the GC in Whidbey... I'm not sure this is a pretty face...。
新增的GC.AddMemoryPressure函数和GC.RemoveMemoryPressure函数可以提示GC当前Unmanaged资源的使用情况,以便GC判断在合适的时候进行回收工作;以前供WinForm内部使用的HandleCollector类也公开允许用户使用,用于管理Unmanaged资源的自动回收。
对于CLR/JVM这种使用垃圾回收机制的环境,如何处理其无法管理的外部资源类型是一件让人头痛的事情。例如在使用Win32的位图(BITMAP)资源时,在Managed对象中只需保存一个句柄,但实际上内存占用跟位图文件大小相关,同时句柄本身的数量也是受到系统本身限制的。而这些Unmanaged资源对GC来说都是不可见的,GC只能看到这座冰山露出水面的一小部分,其余的部分只能靠程序员自觉管理,如使用Dispose模式。但这样的使用就违背了GC的基本原则,必然会出现两种内存管理模型的冲突。Whidbey中新增的这两个GC的增强,实际上就是分别对这两种内存管理模型,提供了与GC兼容的接口。
GC.AddMemoryPressure函数和GC.RemoveMemoryPressure函数是全局性统计的函数,它们必须被使用到Unmanaged资源的CLR对象成对地调用,以保障对资源使用情况的精确跟踪。如果一个对象忘记调用RemoveMemoryPressure函数,则此对象和其Unmanaged资源在被施放后,GC认为以前用AddMemoryPressure函数注册的Unmanaged资源仍在使用,会降低GC的准确率。因此最好使用mihailik给出的封装类UnmanagedResource,通过IDispose和Finalizer确保两个函数的匹配调用。
以下为引用:
public abstract class UnmanagedResource : IDisposable
{
readonly int m_PressureAmount;public UnmanagedResource(int pressureAmount)
: this( pressureAmount, true )
{ }
public UnmanagedResource(int pressureAmount, bool addPressureNow)
{
this.m_PressureAmount=pressureAmount;if( addPressureNow )
ResourceAllocated();
}protected void ResourceAllocated()
{
GC.AddMemoryPressure(PressureAmount);
}protected void ResourceReleased()
{
GC.RemoveMemoryPressure(PressureAmount);
}
protected int PressureAmount
{
get { return m_PressureAmount; }
}
public void Dispose()
{
Dispose(true);
}protected virtual void Dispose(bool disposing)
{
ResourceReleased();if( disposing )
{
GC.SuppressFinalize();
}
}~UnmanagedResource()
{
Dispose(false);
}
}
不过个人认为这种提示的管理粒度过大了,而且过于依赖人的自觉性。不如使用IoC模式的思想,定义一个接口IUnmanagedResource,所有使用Unmanaged资源的类都实现此接口,然后GC提供GC.RegisterForUnmanagedResource函数将对象注册到GC。此接口提供GetUnmanagedResourceSize()函数,让GC了解其Unmanaged资源的使用情况,如
以下为引用:
public interface IUnmanagedResource
{
uint GetUnmanagedResourceSize();
};class Bitmap : IUnmanagedResource
{
private long _size;uint GetUnmanagedResourceSize()
{
return _size;
}Bitmap (string path )
{
_size = new FileInfo(path).Length;GC.RegisterForUnmanagedResource(this);
// other work
}
}
这样的好处是可以将对Unmanaged资源的管理粒度降低到对象一级,并让CLR对象和Unmanaged资源绑定,确保施放CLR对象时能够同步更新整体资源使用情况的统计数据,代价是GC需要维护一个和Finalizer列表类似的UnmanagedResource列表。
在实现上GC.AddMemoryPressure函数和GC.RemoveMemoryPressure函数更新由GC的一个子类MemoryWatcher维护的几个统计值,并根据一定的策略触发GC的回收条件。
System.Runtime.InteropServices.HandleCollector实现则比较简单,构造函数中指定阈值,自身维护一套计数器,在超出指定范围后回收句柄。使用方法很简单,如下:
以下为引用:
// HandleCollector(string name, int initialThreshold, int maximumThreshold);class XXX
{
static readonly HandleCollector GdiHandleType =
new HandleCollector( “GdiHandles”, 10, 50);static IntPtr CreateSolidBrush()
{
IntPtr temp = CreateSolidBrushImpl(…);GdiHandleType.Add();
return temp;
}
internal static void DeleteObject(IntPtr handle)
{
DeleteObjectImpl(handle);GdiHandleType.Remove();
}
}
有兴趣的朋友可以进一步看看这个例子:
http://www.gotdotnet.com/userfiles/chrisan/HandleCollector.zip