为什么GC大对象堆中对象之前都有一个FREE 块?

如果大家经常看 LOH 堆上的对象布局时,会发现一个现象,那就是每一个大对象之前都有一个固定大小的 Free 块,接下来我们一起来探究下,首先上一段简单的代码:.

 public class Program
    {
        public static void Main()
        {
            long[] num1 = new long[20000];

            long[] num2 = new long[20000];

            long[] num3 = new long[20000];

            long[] num4 = new long[20000];

            long[] num5 = new long[20000];

            long[] num6 = new long[20000];

            long[] num7 = new long[20000];

            long[] num8 = new long[20000];

            long[] num9 = new long[20000];

            long[] num10 = new long[20000];

            Console.ReadLine();
        }
    }

接下来我们用 windbg 看下 10 个大对象在 LOH 的布局。

0:009> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x02981018
generation 1 starts at 0x0298100C
generation 2 starts at 0x02981000
ephemeral segment allocation context: none
 segment     begin  allocated  committed  allocated size  committed size
02980000  02981000  02995D84  029A2000  0x14d84(85380)  0x21000(135168)
Large object heap starts at 0x03981000
 segment     begin  allocated  committed  allocated size  committed size
03980000  03981000  03B07B50  03B08000  0x186b50(1600336)  0x187000(1601536)
Pinned object heap starts at 0x04981000
04980000  04981000  04983220  04992000  0x2220(8736)  0x11000(69632)
Total Allocated Size:              Size: 0x19daf4 (1694452) bytes.
Total Committed Size:              Size: 0x1a8000 (1736704) bytes.
------------------------------
GC Allocated Heap Size:            Size: 0x19daf4 (1694452) bytes.
GC Committed Heap Size:            Size: 0x1a8000 (1736704) bytes.
0:009> !dumpheap 03981000  03B07B50
 Address       MT     Size
03981000 00692c80       12 Free
03981010 00692c80       16 Free
03981020 05ca4b08   160012     
039a8130 00692c80       16 Free
039a8140 05ca4b08   160012     
039cf250 00692c80       16 Free
039cf260 05ca4b08   160012     
039f6370 00692c80       16 Free
039f6380 05ca4b08   160012     
03a1d490 00692c80       16 Free
03a1d4a0 05ca4b08   160012     
03a445b0 00692c80       16 Free
03a445c0 05ca4b08   160012     
03a6b6d0 00692c80       16 Free
03a6b6e0 05ca4b08   160012     
03a927f0 00692c80       16 Free
03a92800 05ca4b08   160012     
03ab9910 00692c80       16 Free
03ab9920 05ca4b08   160012     
03ae0a30 00692c80       16 Free
03ae0a40 05ca4b08   160012     

Statistics:
      MT    Count    TotalSize Class Name
00692c80       11          172      Free
05ca4b08       10      1600120 System.Int64[]
Total 21 objects

可以看到每一个大对象前面都有一个 16 byte 的FREE块,那为什么是 16byte呢,这是因为32bit进程预留了 4*4byte 大小,在 64bit 平台上是 4*8=32byte,接下来可以用 dc 看下 free 块的内容。

0:009> dc 03ae0a30
03ae0a30  00692c80 00000004 00000000 00000000  .,i.............

算是清零内容,那free块有什么用呢?主要还是 GC 回收的 计划阶段 时用于模拟压缩用的 Plug 信息,存放 reloc 地址值。

如果你把 long[20000] 改成 long[2] 的时候,你会发现这 10 个 long[2] 都是紧凑在一块的。

0:000> !dumpheap 0285acac  0285ada8
 Address       MT     Size
0285acac 05ca4b08       28     
0285acc8 05ca4b08       28     
0285ace4 05ca4b08       28     
0285ad00 05ca4b08       28     
0285ad1c 05ca4b08       28     
0285ad38 05ca4b08       28     
0285ad54 05ca4b08       28     
0285ad70 05ca4b08       28     
0285ad8c 05ca4b08       28     
0285ada8 05ca4b08       28     

Statistics:
      MT    Count    TotalSize Class Name
05ca4b08       10          280 System.Int64[]
Total 10 objects