1. 简介:
Glib中使用了slab进行内存管理(代码解释见[1],cache coloring见[2]),同时引入了magazine cache来进行多级缓存。本文主要介绍magazine cache部分实现,不讨论slab和使用malloc的实现代码。
2. 层级关系:
采用了magazine layer后的glib内存管理,在整体上一共分为四个层级:
1. 线程缓存magazine层,这一层为线程专属,每个线程维护自己的两个空闲链表,内存申请和释放首先与这一级的缓存进行交付。
2. 全局缓存magazine层,这一层为全局的缓存,当某个线程缓存空了需要申请内存,会先向全局缓存进行申请,当某个线程的空闲链表都满了,会将其中一级缓存释放回全局缓存层。
3. slab层。
4. 系统层。
3. 数据结构:
magazine本身数据结构上看起来并不复杂,但是其在实际使用时引入了一些复杂的操作。
// 这里的chunk不一定是以普通单链表存在,也可能在data中存在一条子链
// 当magazine归还到全局缓存时,data还会用于元数据的存储和管理
struct _ChunkLink {
ChunkLink *next;
ChunkLink *data;
};
// 用于空闲列表的管理,chunks里面记录的都是空闲chunk。
typedef struct {
ChunkLink *chunks;
gsize count; /* approximative chunks list length */
} Magazine;
// 线程缓存,一共设有二级缓存,优先从一级缓存分配,归还到二级缓存。
// 其实是两个数组,在内存排布上ThreadMemory紧跟着就是两个大小的数组。
// ThreadMemory {
// Maganize cache 1 [n_magazines]; --------------|
// Maganize cache 2 [n_magazines]; --------| |
// } | |
// Maganize [0] <---------------------------|-----|
// .... |
// Maganize [n_magazines - 1] |
// Maganize [n_magazines] <-----------------|
// ....
// Maganize [2 * n_maganizes - 1]
typedef struct {
Magazine *magazine1; /* array of MAX_SLAB_INDEX (allocator) */
Magazine *magazine2; /* array of MAX_SLAB_INDEX (allocator) */
} ThreadMemory;
当一个magazine放到线程缓存中,这个magazine中所有的chunk都有可能随时拆分使用,因此这时候的magazine单纯的是一个chunk链表,但是可能是不规则的链表。如下图:
当这个magazine归还给全局magazine的时候,这时候的magazine都处于空闲状态,因此归还时会调整chunk链表的链接形态,用前四个(因此一个magazine最少有4个chunk)chunk来做元数据管理,分别代表上一个magazine,归还时间戳,下一个magazine,magazine中的chunk数。如下图:chunk 1、2、3、4位置改变,成为普通链表的形态,并用他们的data来保存元数据。
线程缓存的magazine是一个一个放置在一组数组中的,因此不需要维护前后关系,如下图:
而全局magazine缓存则类似一个桶,在对应id下会有若干个magazine串成一组链表,而链表之间的关系则由上面说的元