我的一位同事声称代表总是固定在 .net 中的堆中。我似乎无法找到有关此信息是否真实的任何权威来源。
这是真的吗?
我的一位同事声称代表总是固定在 .net 中的堆中。我似乎无法找到有关此信息是否真实的任何权威来源。
这是真的吗?
委托本身没有被固定,并且可以被 GC 自由移动。但是,这并不一定意味着您需要固定它。
在 JIT 时,生成的非托管代码以及如果您使用非托管函数指针来调用委托的代码时实际使用的代码,将不会被移动。Chris Brumme(CLR 团队的)在他的博客上详细解释了这一点(这里的关键句是This stub exists in fixed memory outside the GC heap.):
同样,托管委托可以编组为非托管代码,在其中它们作为非托管函数指针公开。对这些指针的调用将执行非托管到托管的转换;调用约定的改变;进入正确的 AppDomain;以及任何必要的参数编组。显然,非托管函数指针必须指向一个固定地址。如果 GC 重新定位它,那将是一场灾难!这导致许多应用程序为委托创建固定句柄。这是完全没有必要的。非托管函数指针实际上是指我们动态生成以执行转换和封送处理的本机代码存根。此存根存在于 GC 堆外的固定内存中。
但是,应用程序负责以某种方式延长委托的生命周期,直到不再有来自非托管代码的调用。本机代码存根的生命周期与委托的生命周期直接相关。一旦委托被收集,通过非托管函数指针的后续调用将崩溃或以其他方式破坏进程。在我们最近的版本中,我们添加了一个客户调试探针,它允许您干净地检测代码中的这个 - 太常见的 - 错误。如果您在开发过程中还没有开始使用 Customer Debug Probes,请看一下!
所以你的同事在技术上是不正确的 - 代表可以移动,而不是固定。但是,这位同事在精神上是“正确的”——如果您使用非托管函数指针来调用委托,则不一定需要固定委托,尽管您确实需要保证委托的生命周期。
这是不正确的。代表未固定在 .Net 中。
当代理用作 PInvoke 函数的参数时,代理就像被固定一样。CLR 分配一个本地 thunk,其中包含对委托的引用。但是这个引用不会固定委托,也不会充当对对象的强引用。
请注意,固定对象不是免费的。它实际上给 GC 带来了相当大的成本。小剂量也可以,但固定每个 .Net 代表可能会对性能产生非常明显的影响