. net运行时是如何移动内存的
本文关键字:移动 内存 运行时 何移动 net | 更新日期: 2023-09-27 17:51:09
众所周知,. net垃圾收集器不仅"删除"堆上的对象,而且还使用内存压缩来对抗内存碎片。据我所知,基本上内存被复制到一个新的地方,旧的地方在某个时候被删除。
我的问题是:这是如何工作的?
我最好奇的是GC运行在一个单独的线程中,这意味着当我们执行代码时,我们正在处理的对象可以被GC 移动。
问题的技术细节
为了说明,让我更详细地解释我的问题:
class Program
{
private int foo;
public static void Main(string[] args)
{
var tmp = new Program(); // make an object
if (args.Length == 2) // depend the outcome on a runtime check
{
tmp.foo = 12; // set value ***
}
Console.WriteLine(tmp.foo);
}
}
在这个小示例中,我们创建了一个对象并在对象上设置了一个简单的变量。对于这个问题来说,"***"是最重要的:如果"tmp"的地址移动了,"foo"会引用一些不正确的东西,一切都会中断。
垃圾收集器在一个单独的线程中运行。因此,据我所知,'tmp'可以在此指令期间移动,'foo'可以以不正确的值结束。但不知何故,奇迹发生了,却没有发生。
对于反汇编程序,我注意到编译后的程序实际上取了'foo'的地址并移动了值'12:
000000ae 48 8B 85 10 01 00 00 mov rax,qword ptr [rbp+00000110h]
000000b5 C7 40 08 0C 00 00 00 mov dword ptr [rax+8],0Ch
我或多或少希望在这里看到一个可以更新的间接指针——但显然GC比这更聪明。
此外,我没有看到任何线程同步检查对象是否已被移动。那么GC如何更新执行线程中的状态呢?
那么,这是如何工作的呢?如果GC不移动这些对象,定义是否移动这些对象的"规则"是什么?
. net GC(至少部分地)是一个"停止世界" GC:它在工作之前停止托管线程,完成它的工作,然后重新启动托管线程。
"工作站"GC可以是并发的(所以部分不是停止世界),但请注意https://msdn.microsoft.com/library/ee851764.aspx。
当您将工作站垃圾收集与并发垃圾收集一起使用时,回收的对象不会被压缩,因此堆大小可以相同或更大(碎片可以使它看起来更大)。
请注意,对于所有的GC, gen0和gen1总是停止世界。所以他们可以毫无问题地移动记忆块。只有gen2可以在后台通过一些配置的GC完成(这个链接,信息在页面周围有点碎片化),所以总是有一个"世界停止"的时刻,已经释放的内存可以被压缩。