首页 > 代码库 > 托管堆与垃圾收集
托管堆与垃圾收集
一、Windows内存架构简介
在用户态(user mode)中运行的进程通常会使用一个或多个堆托管器。最常见的堆管理器就是Windows堆管理器(windows heap manager)。另一个常见的堆管理器就是CLR堆管理器,它是在.Net应用程序中使用。
Windows堆管理器负责满足大多数的内存分配/回收请求,它从Windows虚拟内存管理器中分配大块内存空间(内存段),并且通过维持特定的数据记录和空闲列表,以一种高效的方式将大块内存空间分割为许多更小的内存块来满足进程的分配请求。CLR堆管理器的功能类似,它为托管进程中的所有内存分配请求提供一站式服务。与Windows堆管理器相似的是,它同样从Windows虚拟内存管理器中分配内存段,然后用这些内存段来满足所有的内存分配/回收请求。这两种堆管理器之间的关键差异在于,二者在维持堆完整性时使用的记录数据的结构是不同的。
从上图看出,CLR堆管理器的运行模式有两种:工作站模式和服务器模式。
服务器模式的主要特点是,它不是只有一个堆,而是每个处理器都对应一个堆,并且堆中内存段的大小通常大于工作站堆中内存段的大小。服务器模式有一个专门的线程在管理所有的GC操作,而工作站模式中,则是在执行内存分配的线程上执行GC操作。
在2.0版本之前,工作站GC是在mscorwks.dll中实现的,而服务器GC则是在mscorsvr.dll中实现的。到了2.0版本,这两种实现被合并到同一个二进制文件中(mscorwks.dll)。这是二进制级别上的合并。
1、内存段
当加载CLR时,每个托管进程起初都有两个堆,并且每个堆都有各自的内存段。
- 第一个堆是小对象堆(small object heap),这个堆中有一个初始内存段,在工作站模式中的大小为16MB(服务器模式更大)。小对象堆用来荣达大小不超过85KB的对象。
- 第二个堆被称为大对象堆(Large Object Heap,LOH),它同样有一个初始内存段,大小为16MB。LOH用来容纳大于等于85KB的对象。
需要注意的是,当创建一个内存段时,不会立即提交段中的所有内存,CLR堆管理器会保留一部分内存空间,并且根据需要来进行提交。当小对象堆中的内存都被耗尽时,CLR堆管理器将触发GC操作;如果内存空间仍然不足,将对堆进行扩展。然而,如果大对象堆中的内存被耗尽时,堆管理器将创建一个新的内存段来提供内存。相应地,当垃圾收集器释放内存时,内存段中的空间可以回收,当一个内存段中的所有空间都被回收时,这个内存段就会被彻底释放。
通过内存地址来查看内容的SOS命令是!address。
驻留在托管堆上的每个对象都附带一些元数据。具体来说,每个对象的前面都有8个字节。
在托管堆上的每个对象中,前4个字节是同步块(Sync Block)索引,后面4个字节是方法表(Method Table)的指针。
2、分配内存
在不需要执行GC就可以成功分配内存时,满足内存分配请求将是一个非常高效的过程。这种情况下会执行两个主要的任务,即递进内存分配指针和清空相应的内存区域。
递进内存分配指针:新分配的内存将紧跟内存段中最后一个已分配的对象。当另一个分配请求被满足时,内存分配指针会再次被递进,依次类推。
这种分配模式与Windows堆管理器中的模式有着极大的不同,因为Windows堆管理器并不会像这样来管理各个对象之间的位置。在Windows堆管理器中,当一个分配请求到来时,可以使用内存段中的任何一块空闲内存来满足这个请求。