前端分配器(Front End Allocator)是后端分配器(Back End Allocator)的一个抽象优化层。 通过允许有不同类型的前端分配器,有着不同内存需求的应用程序可以选择合适的分配器。 例如,如果一个应用程序会存在频繁的内存分配,那么它可以选择使用低碎片(Low Fragmentation)前端分配器来避免堆碎片。在Windows中有两个不同的前端分配器可供使用:
1 旁视列表(Look aside list,LAL)前端分配器
2 低碎片(Low fragmentation,LF)前端分配器
除了Vista,其他的Windows版本默认都使用LAL前端分配器。在Vista中,设计上决定默认使用LF前端分配器。LAL是一个由128个单向链表组成的表。每个单向链表包含特定大小的自由堆块(free heap blocks),块的大小由16bytes开始。每个堆块包含8bytes的堆块元数据(heap block metadata)用于管理堆块。例如,如果一个24Bytes的分配请求到达前端分配器,前端分配器将会寻找32Bytes大小的自由块(free blocks),32Byte = 24Byte用户请求数据+8Byte元数据。由于每一个堆块自身都需要占据8Byte的元数据,所以可以返回给调用者的最小堆块的大小应该为16bytes(因为8Byte的自由堆块已包含了8Byte的元数据,没有空间去供用户使用了)。那么,前端分配器不会使用旁视表(Look-aside Table)中的索引0(原文中为index 1,但根据理解应该为index 0)。
表中每一个索引代表一系列特定大小的自由堆块,后一索引代表的堆块的大小比前一索引代表的堆块大小多8byte。(index1--16B,index2--24B,index3--32B,...,index127--1023Byte)。当应用程序释放一个内存块,堆管理器将标记该内存块为“自由”(free),并且把这个释放的内存块放到前端分配器的旁视表中对应的索引上去。 如果下一次请求内存的时候,前端分配器将会查看在旁视表中是否有可用的(大小相同)自由堆块,如果有就可以将其返回给调用者。不用说,通过旁视列表前端分配器来满足内存分配将是速度最快的。
看一个例子,如下图所示,是某个时刻旁视列表的状态:
表中的index0当然是无用的,index1代表的自由堆块的大小为16Bytes(8Bytes为可用空间),共有三个,index 3 代表的自由块的大小为32Bytes(24Bytes为可用空间),共两个。
若要分配一个24Bytes的内存块,经过计算(24B+8B)/8B-1=3,堆管理器将会知道需要从索引3中寻找对应的内存块。找到后堆管理器将列表中的第一个堆块移除并返回给调用者。
若要分配一个16Bytes的内存块,经过计算,对应的索引应该为index 2,但此刻index2是个空列表,无法从前端分配器中分配内存,那么这个内存请求将会向下转发到后端分配器中做进一步处理。