. 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运行时是如何移动内存的

. net GC(至少部分地)是一个"停止世界" GC:它在工作之前停止托管线程,完成它的工作,然后重新启动托管线程。

"工作站"GC可以是并发的(所以部分不是停止世界),但请注意https://msdn.microsoft.com/library/ee851764.aspx。

当您将工作站垃圾收集与并发垃圾收集一起使用时,回收的对象不会被压缩,因此堆大小可以相同或更大(碎片可以使它看起来更大)。

请注意,对于所有的GC, gen0和gen1总是停止世界。所以他们可以毫无问题地移动记忆块。只有gen2可以在后台通过一些配置的GC完成(这个链接,信息在页面周围有点碎片化),所以总是有一个"世界停止"的时刻,已经释放的内存可以被压缩。