带内存压力的SafeHandle

时间:2016-08-03 21:24:05

标签: c# .net garbage-collection safehandle constrained-execution-reg

SafeHandle类允许通过P / Invoke marshaller的引用计数和协作安全访问.NET下的本机资源,以避免过早丢弃可能导致应用程序崩溃的句柄。

在某些情况下,通知垃圾收集器某些本机资源正在使用大量内存将是有利的,例如,当" handle"问题是包装的本机库拥有的大缓冲区。 GC.AddMemoryPressure可用于此目的;但是,GC.RemoveMemoryPressure必须在"句柄"的生命周期结束时调用。

这两种方法似乎不兼容:SafeHandleCriticalFinalizerObject,其ReleaseHandle方法在约束执行区(CER)中运行。由于GC.RemoveMemoryPressure没有ReliabilityContractAttribute,所以不能从CER调用它(我猜在关键的最终确定时间,GC的一些与内存压力有关的部分可能无法使用)。

我想到了两种方法,两者都非常不优雅:

  1. SafeHandle可以包装到另一个具有非关键终结器(和Dispose方法)的对象中,该对象可以添加和消除内存压力。这有两个缺点:首先,有人可以处置SafeHandle(它必须暴露给传递给本机方法)而不需要处理包装器,因此内存压力被高估了。其次,当SafeHandle还活着时,包装器可能会被取消引用,因此内存压力被低估(可能更严重)。

  2. SafeHandle有一个非关键可终结类型的字段来管理内存压力。在这种情况下,上面的大多数问题都消失了,但是,句柄不能在其Dispose或ReleaseHandle方法中处理内存压力对象,因为这会导致CER中的GC.RemoveMemoryPressure调用。可以提供另一种方法(例如DisposeNoncriticial),但这会导致切片"将句柄强制转换为IDisposable或在使用块中使用时出现问题。

  3. 是否存在创建具有相关内存压力的SafeHandle的常见模式?

1 个答案:

答案 0 :(得分:2)

这是一个很好的模式来包装句柄,而不是在正常情况下暴露它。你可以像FileStream那样做那部分。据了解,句柄归FileStream所有,关闭它是违法的。这不是强制执行,但它是合同。

第二个问题是您希望将SafeHandle的生命周期与其包装器联系起来。 ConditionalWeakTable类允许您这样做。设计看起来像这样:

class Wrapper {
 SafeHandle handle;
 CWT cwt = new CWT() { { handle, this } };

 ~Wrapper() { RemoveMemoryPressure(); }
}

如果句柄处于活动状态,CWT会使包装器保持活动状态。 CWT用于将任意状态与不可扩展的对象相关联,并且没有任何GC效果。

我不确定这一切是否值得,但它(或大部分)满足您的要求将是一个解决方案。

另外,我不确定记忆压力在多大程度上具有实际用途。