委托对象是否总是被固定?

3
我的一位同事声称在.net中,委托始终被固定在堆上。我似乎找不到任何权威的信息来证明这个说法是否正确。请问这是真的吗?
2个回答

4
这是不正确的说法。在.Net中,委托并没有被固定。
当委托作为PInvoke函数的参数时,它会表现得像被固定了一样。CLR会分配一个本地thunk来保存对委托的引用。但是这个引用并不会固定委托,也不会作为对象的强引用。

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx#S7

请注意,固定对象不是免费的。它实际上会给垃圾回收器带来相当大的负担。小量使用是可以的,但让每个.Net委托都被固定可能会对性能产生非常明显的影响。

4

委托本身没有被固定,可以由GC移动。但这并不一定意味着您需要将其固定。

在JIT时间,生成的非托管代码以及如果使用非托管函数指针调用委托代码时实际使用的代码将不会被移动。CLR团队的Chris Brumme在他的博客中详细解释了这一点(关键句子是:该存根存在于GC堆之外的固定内存中。):

同样的道理,托管委托可以转换为非托管代码,并作为非托管函数指针暴露。对这些指针的调用将执行从非托管到托管的转换;调用约定的更改;进入正确的AppDomain;以及任何必要的参数转换。显然,非托管函数指针必须引用固定地址。如果GC正在重定位它,那将是一场灾难!这导致许多应用程序为委托创建一个固定句柄。这是完全不必要的。实际上,非托管函数指针引用一个本地代码存根,我们动态生成该存根以执行转换和转换。该存根存在于GC堆之外的固定内存中。
但是,应用程序需要确保延长委托的生命周期,直到不再从非托管代码中进行调用。本地代码存根的生命周期与委托的生命周期直接相关。一旦收集了委托,通过非托管函数指针进行的后续调用将导致崩溃或损坏进程。在我们最近的版本中,我们添加了一个客户调试探针,允许您在代码中干净地检测到这个过于常见的错误。如果您还没有在开发过程中开始使用客户调试探针,请看一下!

所以你的同事在技术上并不正确——委托可以被移动,而不是固定的。但是,从精神上讲,同事是“正确”的——如果你使用非托管函数指针调用委托,你不一定需要固定委托,但你需要保证委托的生命周期。


从某种意义上说,他可能在精神上是正确的,但他也将其用作避免在服务器代码中使用委托的理由,因为会产生额外的GC开销。有了这些信息,我不认为这个论点真的站得住脚。 - recursive
1
@recursive 是的 - 我同意,没有理由避免使用委托。我只是想指出这很可能是你的同事有这个错误假设的原因,这可能有助于教育他们。 - Reed Copsey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接