English 简体中文 繁體中文 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french
查看: 13|回复: 0

.NET Core GC压缩(compact_phase)底层原理浅谈

[复制链接]
查看: 13|回复: 0

.NET Core GC压缩(compact_phase)底层原理浅谈

[复制链接]
查看: 13|回复: 0

392

主题

0

回帖

1184

积分

管理员

积分
1184
溯源设备

392

主题

0

回帖

1184

积分

管理员

积分
1184
2025-2-6 23:11:23 | 显示全部楼层 |阅读模式
简介

终于来到了GC的最后一个步骤,在此之间,大量预备工作已经完成。万事俱备,只欠东风
清除

如果GC决定不压缩,它将仅执行清除操作。清除操作非常简单,把所有不可到达对象(gap),转换成Free。也就是转换成空闲内存空间。
由于所有的繁重计算任务在plan_phase阶段均已完成,所以步骤比较简单

  • 基于gap的size创建空闲列表
    free > 2 * min_obj_size 的Free块会被放入空闲列表,小于此大小的不再被利用,但会纳入内存碎片统计
  • 恢复“被销毁”的前置plug和plug
    这是pinned 对象的特殊情况,pinned的plug前面可能还是一个plug,所以没有gap来存放, 因此会根据实际情况“钉住”它的前面或者后面的Plug.来暂存gap_reloc_pair信息。 所以用完后还要“还回去”
  • 更新终结队列,并提升或降低plug的代
  • 更新段空间

眼见为实


压缩

如果GC决定压缩,就比较复杂了。总体分为两步

  • 复制对象并移动到新位置(重定位阶段)
  • 将新对象的地址在root上更新
GC重定位阶段

此步骤更新所有对稍后要移动对象的引用,为了更新这些地址,要扫描他们的root,并逐一更新

  • 栈空间的root
  • 跨代记忆集的root
  • 托管堆中的root
  • 前置plug与后置Plug的root
  • 终结器队列的root
  • 句柄表的root
比如某个对象的内存地址为0x1000,压缩后它的新地址为0x500。那就就要对该对象的所有root更新内存地址。
眼见为实

点击查看代码    internal class Program    {        static void Main(string[] args)        {            Append();            AppendStatic();            Compact();        }        public static Person person;        public static List<byte[]> list = new List<byte[]>();        static void Append()        {            //填 10M 数组到 临时段上            for (int i = 0; i < 1024 * 10; i++)            {                list.Add(new byte[1000]);            }            Console.WriteLine("1. 10M 数据已分配完毕,请查看临时段大小,准备分配 Person 对象!");            Debugger.Break();        }        static void AppendStatic()        {            person = new Person();            list = null;            Console.WriteLine("2. Person 已分配,list已去根,请再次观察托管堆!准备触发 GC,请下 compact_phase 断点!");            Debugger.Break();        }        static void Compact()        {            GC.Collect(2, GCCollectionMode.Forced, true, true);            Console.WriteLine("3. GC 已触发,请观察 Person 是否已变!");            Debugger.Break();        }    }    public class Person { }在bp coreclr!WKS::gc_heap::compact_phase 下断点,观察对象的新老地址变化
GC前:

GC后:内存地址发生变化

眼见为实


压缩对象

在上面更新root的操作完成后,GC要移动所有对象。由以下几个步骤组成

  • 复制对象
  • 恢复“被销毁”的前置plug和plug
  • 重新划分代边界
  • 释放内存段
  • 创建空闲列表
眼见为实

GC前:

GC后:对象被移动,原有地址被压缩释放

眼见为实:复制连续的内存区域

以滑动的方式来copy内存,避免出现覆盖问题
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|智能设备 | 粤ICP备2024353841号-1

GMT+8, 2025-3-10 15:00 , Processed in 2.928371 second(s), 30 queries .

Powered by 智能设备

©2025

|网站地图