如何使JIT将堆栈变量扩展到作用域的末尾(GC太快)

时间:2011-08-01 12:22:14

标签: c# .net garbage-collection jit

我们在.Net程序中处理GC太快了。 因为我们使用具有本机资源的类而我们不调用GC.KeepAlive(),所以GC在Native访问结束之前收集对象。结果程序崩溃了。 我们遇到了如下所述的问题:

Does the .NET garbage collector perform predictive analysis of code?

像这样:

{  var img = new ImageWithNativePtr();
   IntPtr p = img.GetData();
   // DANGER!
   ProcessData(p);
}

重点是:JIT生成的信息显示GC在运行GetData()时未使用img。如果GC-Thread在合适的时间出现,它会收集img并且程序崩溃。可以通过附加GC.KeepAlive(img)来解决这个问题。 不幸的是,已经写了太多代码(在太多的地方)来轻松纠正这个问题。

因此:例如是否有一个属性(即ImageWithNativePtr)使JIT在Debug构建中表现得像?在Debug构建中,变量img将保持有效,直到scope(})结束,而在Release中,它会在注释DANGER中失去有效性。

3 个答案:

答案 0 :(得分:2)

据我所知,无法根据方法引用的类型来控制抖动的行为。您可以将方法本身归属,但这不会填满您的订单。既然如此,你应该咬紧牙关并重写代码。 GC.KeepAlive是一种选择。另一种方法是让GetData返回一个安全句柄,该句柄将包含对象的引用,并且ProcessData接受句柄而不是IntPtr - 这无论如何都是好的做法。然后GC将保持安全句柄,直到方法返回。如果您的代码片段中的大多数代码都有var而不是IntPtr,那么您甚至可以在不修改每种方法的情况下离开。

答案 1 :(得分:1)

您有几个选择。

  1. (需要工作,更正确) - 在IDisposable类上实施ImageWithNativePtr,因为它会向下编译为try { ... } finally { object.Dispose() },如果您使用{{{{}}更新代码,这将使对象保持活动状态1}}秒。你可以通过安装像CodeRush这样的东西来缓解这样做的痛苦(即使是免费的Xpress支持这个) - 它支持创建using块。
  2. (更容易,不正确,更复杂的构建) - 使用Post Sharp或Mono.Cecil并创建自己的属性。通常,此属性会导致GC.KeepAlive()插入到这些方法中。
  3. CLR没有为此功能内置任何内容。

答案 2 :(得分:0)

我相信您可以使用实现IDispose的容器和using语句来模拟您想要的内容。 using语句允许定义范围,您可以在其中放置任何需要在该范围内保持活动的内容。如果您无法控制ImageWithNativePtr的实现,这可能是一种有用的机制。

要处理的东西的容器是一个有用的习语。特别是当你真的应该处理某些东西时......可能是图像的情况。

using(var keepAliveContainer = new KeepAliveContainer())
{
  var img = new ImageWithNativePtr();
  keepAliveContainer.Add(img);
  IntPtr p = img.GetData();
  ProcessData(p);
  // anything added to the container is still referenced here.
}