如何使JIT将堆栈变量扩展到作用域的末端(GC太快了)
本文关键字:GC JIT 何使 堆栈 变量 作用域 扩展到 | 更新日期: 2023-09-27 18:04:22
我们正在处理。net程序中GC速度太快的问题。因为我们使用带有本机资源的类,并且我们不调用GC. keepalive(),所以GC会在本机访问结束之前收集对象。结果导致程序崩溃。我们遇到了如下所述的问题:
.NET垃圾收集器执行代码的预测分析吗?
像这样:
{ var img = new ImageWithNativePtr();
IntPtr p = img.GetData();
// DANGER!
ProcessData(p);
}
关键在于:JIT生成的信息向GC显示,当GetData()运行时img没有被使用。如果GC-Thread在正确的时间出现,它将收集内存并导致程序崩溃。我们可以通过附加GC.KeepAlive(img)来解决这个问题;不幸的是,已经有太多的代码编写(在太多的地方)来纠正这个问题很容易。
因此:是否有例如属性(即ImageWithNativePtr)使JIT的行为像在调试构建?在调试版本中,变量img在作用域(})结束之前仍然有效,而在发布版本中,它在注释DANGER处失去有效性。
据我所知,没有办法根据方法引用的类型来控制抖动的行为。您可能能够为方法本身赋予属性,但这无法满足您的订单。既然如此,你就应该咬紧牙关重写代码。GC.KeepAlive
是一种选择。另一种方法是让GetData
返回一个安全句柄,该句柄将包含对对象的引用,并让ProcessData
接受该句柄而不是IntPtr
-这是一个很好的实践。然后,GC将保持安全句柄直到方法返回。如果您的大部分代码都有var
而不是代码片段中的IntPtr
,那么您甚至可以不修改每个方法而获得成功。
你有几个选择。
- (需要工作,更正确)-在你的
ImageWithNativePtr
类上实现IDisposable
,因为它编译到try { ... } finally { object.Dispose() }
,这将保持对象存活,如果你用using
s更新你的代码。你可以通过安装CodeRush(甚至免费的Xpress也支持)来减轻这样做的痛苦-它支持创建using
块。 - (更容易,不正确,更复杂的构建)-使用后尖锐或单声道。Cecil并创建自己的属性。通常这个属性会导致GC.KeepAlive()被插入到这些方法中。
CLR没有内置此功能
我相信您可以用实现iddispose的容器和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.
}