jemalloc源码解读(五)内存布局


    从《更好的内存管理-jemalloc》这边文章中,可以比较直接看到,在Jemalloc中,几个重要对象的关系,arena/bin/run之间关系。但是他没有揭示更深刻的东西。我们知道,arena包含多个bin,而bin也包含多个run。对于任何一个请求,根据size计算出,所在bin数组的下表。然后从这个bin中,提取合适的run来分配内存。这个大方向没有错,但如何实现就很讲究,下面我们来更深入的分析。

一、run的结构

   header + bitmap + region0 + ...+ regionx

  这个内存布局是动态的,根据arena_bin_info来确定的。

二、bin的结构  

struct arena_bin_s {
	malloc_mutex_t	lock;
	arena_run_t	*runcur;
	arena_run_tree_t runs;
};
lock和runcur就没有什么好说的,最好玩的就是runs,后面再分析。

三、arena的结构

struct arena_s {
	malloc_mutex_t		lock;
	
	arena_chunk_tree_t	chunks_dirty;
	arena_chunk_t		*spare;
	
	arena_avail_tree_t	runs_avail;	
	arena_bin_t		bins[NBINS];
};

这里做了些裁剪,没有全部列出。到此为止,《更好的内存管理-jemalloc 》都介绍过了。我们来看更精彩的内容,我在上面的free过程分析中,一直强调chunk的分配方式是jemalloc的一个非常重要的基础,他的要点就是内存地址对齐,简单说,就是对于一个地址addr,通过CHUNK_ADDR2BASE宏就能直接得到所在的chunk。


下面,我们假设一个应用场景,我要分配一个大小为SIZE内存块,那么流程就是这样:

1、选定一个arena或者tcache,没啥好说的。

2、计算对应的对齐长度,选定arena中bins的下表。

3、从runcur或者runs选择一个run

4、从选定的run中,计算bitmap,得到空闲的region,后返回。

在正常情况下,这是个很完美的流程,没啥好说。但是出现没有足够内存情况下,问题就来了。需要从系统拉出一个新的Chunk来。让我们继续研究下chunk的结构,

struct arena_chunk_s {
	arena_t			*arena;

	rb_node(arena_chunk_t)	dirty_link;
	size_t			ndirty;
	size_t			nruns_avail;

	size_t			nruns_adjac;

	arena_chunk_map_t	map[1]; 
};
在这个结构中,map的结构是最精彩的。首先map是个数组,他的下标对应于run在这个chunk中的位置,从代码

static arena_run_t * arena_bin_runs_first(arena_bin_t *bin)
{
	arena_chunk_map_t *mapelm = arena_run_tree_first(&bin->runs);
	if (mapelm != NULL) {
		arena_chunk_t *chunk;
		size_t pageind;
		arena_run_t *run;

		chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm);
		pageind = ((((uintptr_t)mapelm - (uintptr_t)chunk->map) /
		    sizeof(arena_chunk_map_t))) + map_bias;
		run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -
		    arena_mapbits_small_runind_get(chunk, pageind)) <<
		    LG_PAGE));
		return (run);
	}

	return (NULL);
}

中可以看到,CHUNK_ADDR2BASE宏又一次被完美应用。在前面,我们提到bin->runs是个最有意思的成员变量。因为runs里面的节点不是run而是run对应的arena_chunk_map_t,这个类型是对run的描述。他们之间的关系是pageind。计算mapelm到map的相对位置可以得到pageind,计算run到chunk的相对位置,同样可以得到相同的pageind。


解析到这里,我们可以得到几个重要结论。

其一、jemalloc的一个重要基石,就是CHUNK_ADDR2BASE,省了很多查找。

其二、chunk是从系统分配内存的的最小单位,但他本身包含了描述信息。

其三、run是内存分配的单元,其本身也包含了描述信息。


在jemalloc中,描述和数据的混合是一个重要特点。也是一个无奈之举,可以说是CHUNK_ADDR2BASE这把双刃剑的结果。




你可能感兴趣的:(jemalloc源码解读(五)内存布局)