.NET 垃圾收集器收集对象(回收它们的内存)并执行内存压缩(以将内存碎片保持在最低限度)。
我想知道,由于一个应用程序可能有很多对对象的引用,当对象的地址由于 GC 进行的压缩而发生变化时,GC(或 CLR)如何管理这些对对象的引用。
最佳答案
这个概念很简单,垃圾收集器简单地更新任何对象引用并将它们重新指向移动的对象。
实现有点棘手, native 代码和托管代码之间没有真正的区别,它们都是机器代码。对象引用并没有什么特别之处,它只是运行时的一个指针。收集器需要一种可靠的方法来找回这些指针并将它们识别为引用托管对象的类型。不仅要在压缩过程中移动指向的对象时更新它们,还要识别实时引用以确保不会过早收集对象。
对于存储在 GC 堆上的类对象中存储的任何对象引用来说,这很简单,CLR 知道对象的布局以及哪些字段存储指针。对于存储在堆栈或 cpu 寄存器中的对象引用就没那么简单了。像局部变量和方法参数。
执行托管代码使其与 native 代码不同的关键属性是 CLR 可以可靠地迭代托管代码拥有的堆栈帧。通过限制用于设置堆栈框架的代码类型来完成。这在 native 代码中通常是不可能的,“帧指针省略”优化选项特别讨厌。
堆栈框架遍历首先让它找到存储在堆栈中的对象引用。并让它知道该线程当前正在执行托管代码,以便也应该检查 cpu 寄存器的引用。从托管代码到 native 代码的转换涉及在收集器识别的堆栈上写入一个特殊的“cookie”。所以它知道不应检查任何后续堆栈帧,因为它们将包含从未引用托管对象的随机指针值。
当您启用非托管代码调试时,您可以在调试器中看到这一点。查看“调用堆栈”窗口并注意 [Native to Managed Transition] 和 [Managed to Native Transition] 注释。那是识别那些 cookie 的调试器。对它来说也很重要,因为它需要知道 Locals 窗口是否可以显示任何有意义的东西。 Stack Walk 也在框架中公开,请注意 StackTrace 和 StackFrame 类。这对于沙盒非常重要,代码访问安全 (CAS) 执行堆栈遍历。
关于c# - 发生压缩后GC如何更新引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10669173/