c# - 当垃圾收集器在堆中移动数据时,引用是否会更新?

标签 c# .net garbage-collection heap-memory

我读到 GC(垃圾收集器)出于性能原因在堆中移动数据,我不太明白为什么,因为它是随机存取内存,可能是为了更好的顺序访问,但我想知道堆栈中的引用是否在这种情况下得到更新堆中发生移动。但也许偏移地址保持不变,但数据的其他部分被垃圾收集器移动,不过我不确定。

我认为这个问题与实现细节有关,因为并非所有垃圾收集器都可以执行此类优化,或者它们可以执行但不会更新引用(如果这是垃圾收集器实现中的常见做法)。但是我想得到一些特定于 CLR(公共(public)语言运行时)垃圾收集器的总体答案。

我还阅读了 Eric Lippert 的“引用不是地址”一文 here ,下面的段落让我有点困惑:

If you think of a reference is actually being an opaque GC handle then it becomes clear that to find the address associated with the handle you have to somehow "fix" the object. You have to tell the GC "until further notice, the object with this handle must not be moved in memory, because someone might have an interior pointer to it". (There are various ways to do that which are beyond the scope of this screed.)

对于引用类型,我们不想移动数据。那么我们在堆中还存储了什么,我们可以为了性能优化而四处移动?也许我们存储在那里的类型信息?顺便说一下,如果你想知道那篇文章是关于什么的,那么 Eric Lippert 将引用与指针进行了一点比较,并试图解释说引用只是地址可能是错误的,尽管 C# 是这样实现它的。

另外,如果我上面的任何假设是错误的,请纠正我。

最佳答案

是的,引用在垃圾回收期间得到更新。必然如此,当堆被压缩时对象被移动。压缩有两个主要目的:

  • 它通过更有效地使用处理器的数据缓存来提高程序的效率。这对现代处理器来说是一个非常非常大的问题,RAM 与执行引擎相比非常慢,胖了两个数量级。当处理器必须等待 RAM 提供变量值时,它可能会停止 数百 条指令。
  • 它解决了堆遭受的碎片问题。当释放一个被事件对象包围的小对象时,就会发生碎片。一个孔,只能用于大小相同或更小的物体。不利于内存使用效率处理器效率。请注意 LOH(.NET 中的大对象堆)如何不被压缩,因此会遇到这种碎片问题。 SO 对此有很多疑问。

不管 Eric 怎么说,对象引用实际上只是一个地址。指针,与您在 C 或 C++ 程序中使用的类型完全相同。非常有效,必然如此。移动对象后 GC 要做的就是更新存储在该指针中的地址到移动的对象。 CLR 还允许为对象分配句柄,额外 引用。在 .NET 中公开为 GCHandle 类型,但仅当 GC 需要帮助确定对象是否应保持事件状态或不应移动时才需要。仅当您与非托管代码互操作时才相关。

找到那个指针并不是那么简单。 CLR 投入了大量资金以确保能够可靠且高效地完成这项工作。这样的指针可以存储在许多不同的地方。更容易找回的是存储在对象字段、静态变量或 GCHandle 中的对象引用。硬的是存储在处理器堆栈或 CPU 寄存器中的指针。例如,发生在方法参数和局部变量上。

要实现这一点,CLR 需要提供的一个保证是 GC 始终可以可靠地遍历线程的堆栈。所以它可以找到存储在堆栈帧中的局部变量。然后它需要知道在哪里查看这样的堆栈帧,这是 JIT 编译器的工作。当它编译一个方法时,它不仅会为该方法生成机器代码,还会构建一个表来描述这些指针的存储位置。您将在 this post 中找到更多详细信息.

关于c# - 当垃圾收集器在堆中移动数据时,引用是否会更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27616910/

相关文章:

.net - 将 pdf 页面提取为 png 的开源选项有哪些?

php - APC 何时删除旧条目?

performance - 标记扫描GC怎么了?

c# - 使用 linq 从正则表达式匹配中获取组名

c# - String.Format 在字符串中存储双引号

c# - 如何在不安装 SQL Server Express Edition 的情况下在 Visual Studio 中添加 SQL Server 数据库文件 (.mdf)?

Java循环引用,糟糕的风格?

c# - 如何在 winform 上刷新数据驱动的组合框

c# - 在分发或编辑器构建中屏蔽某些代码,例如#if defined

c# - 绑定(bind)到现有进程