C# 这里需要GC.KeepAlive,还是可以依靠局部变量和参数使对象保持活动状态?

C# 这里需要GC.KeepAlive,还是可以依靠局部变量和参数使对象保持活动状态?,c#,.net,garbage-collection,unsafe,C#,.net,Garbage Collection,Unsafe,我有很多方法,它们使用不安全的代码,获取WPF的WriteableBitmap,并直接从其BackBuffer读取 现在还不完全清楚当我做这样的事情时是否应该使用GC.KeepAlive: int MyMethod(WriteableBitmap bmp) { return DoUnsafeWork(bmp.BackBuffer); } 一方面,在MyMethod的堆栈上仍然存在对bmp的引用。另一方面,它似乎依赖于实现细节——这可能会编译为尾部调用,例如,在输入DoUnsafeWor

我有很多方法,它们使用不安全的代码,获取WPF的
WriteableBitmap
,并直接从其
BackBuffer
读取

现在还不完全清楚当我做这样的事情时是否应该使用
GC.KeepAlive

int MyMethod(WriteableBitmap bmp)
{
    return DoUnsafeWork(bmp.BackBuffer);
}
一方面,在
MyMethod
的堆栈上仍然存在对
bmp
的引用。另一方面,它似乎依赖于实现细节——这可能会编译为尾部调用,例如,在输入
DoUnsafeWork
时不引用
bmp

类似地,想象以下假设代码:

int MyMethod()
{
    WriteableBitmap bmp1 = getABitmap();
    var ptr = bmp.BackBuffer;
    WriteableBitmap bmp2 = getABitmap();
    return DoUnsafeWork(ptr, bmp2);
}
理论上,在方法返回之前,对
bmp1
的引用一直保留在堆栈上,但同样,它似乎使用了一个实现细节。当然,编译器可以自由地合并
bmp1
bmp2
,因为它们永远不会同时活动,即使编译器从不这样做,抖动仍然可以,而且可能会(例如,通过将它们先存储在同一个寄存器中,然后存储在另一个寄存器中)

因此,一般来说:我应该依赖局部变量/参数作为对对象的有效引用,还是应该始终使用
GC.KeepAlive
来保证正确性

这尤其令人费解,因为,很明显

我应该依赖局部变量/参数作为对对象的有效引用吗

不,你的分析是正确的;jitter完全有权告诉垃圾收集器,当托管代码不再使用本地的内容时,本地的内容就死了

我是否应该始终使用GC.KeepAlive来保证正确性


对。这就是它的作用。

如果在获取后台缓冲区之前调用了WriteableBitmap.Lock,则WriteableBitmap应该被固定,并且底层内存不会被移动。至少这是我对WriteableBitmapAPI的解释

我已经广泛使用了WriteableBitmap,特别是codeplex上的开源WriteableBitmapEx库(公开说明,我曾经为这个库贡献过一次,但它不是我的项目)。这将使用锁定/访问Backbuffer(重复)/解锁/无效模式进行绘制。我从来没有遇到过这种模式的问题,即使backbuffer IntPtr存储在结构中并在应用程序中传递


致以最诚挚的问候,

我知道会出现“锁定/解锁”,如果在阅读时不锁定它,我可能会很淘气(文档只解释了为什么在写作时应该锁定)。但是,这与固定指针无关
BackBuffer
根据MSDN保证是固定的。嗯,我不知道,谢谢!我将在我自己的代码上做一些测试,因为锁定/解锁是一个昂贵的操作:)关于你的问题,我看到GC做了一些奇怪的事情,比如收集仍然在范围内但不再使用的东西,特别是在发布模式下。您的代码示例在发布或调试时是否会得到不同的结果;我还没有看到这种代码的任何实际错误行为。事实上,如果它总是在当前编译器/JIT上工作,我不会感到惊讶。我曾经做过一次单元测试,它收集了一个引用,因为在游标下面它不再在范围内(尽管仍然在同一个范围块中)。构建服务器总是编译发布模式,而开发人员是在调试中测试的,所以它在服务器上偶尔失败(我的意思是断断续续的),但在本地机器上从未失败过!在这个特定的案例中,GC.KeepAlive(ref)需要相关阅读:和来自Raymond Chen的博客。@DanielPryden优秀链接;在询问之前,我用谷歌搜索了第一个,但没有搜索第二个。除了我已经问过的问题之外,它还解释了
这个
也不能使当前对象保持活动状态。