1,首先从内存中申请一块大内存chunk(默认为4M);
2,需要有一个角色来管理申请的多个chunk,这时候arena出场了,它用来管理多个chunk;
3,将内存对象分为三个等级
small <4k;
large [4k,4M);
huge >=4M;
4,为了内存对齐,在small中,将此区间分成 44 档,每次小分配请求归整到某档上。例如,小于8字节的,一律分配 8 字节空间;17-32分配请求,一律分配 32 字节空间。对于上述 44 档,有对应的 44 种 runs。每种 run 专门提供此档分配的内存块(叫做 region),每个run默认大小 为4k。
5,在代码上为了对上述44档方便管理定义了一个长度为44的bin数组,bin[44]={8, ......},则用户申请6字节内存大小时,可以得知应该分配索引为0的bin,即bin[0],但是我们也知道在内存上不可能只有一个8字节大小的内存,可能会需要很多这种8字节内存,这时候就需要有多个8字节的run存在 ,run多个就需要在bin[i]中知道当前所在的run,及后面可用的run(用红黑树管理),这时候bin的结构体信息就需要下面这样定义:
1 struct arena_bin_s { 2 /* 作用域当前数据结构的锁*/ 3 malloc_mutex_t lock; 4 /* 当前正在使用的run */ 5 arena_run_t *runcur; 6 /* 可用的run构成的红黑树, 主要用于runcur用完的时候。在查找可用run时, 7 * 为保证对象紧凑分布,尽量从低地址开始查找,减少快要空闲的chunk的数量。 8 */ 9 arena_run_tree_t runs; 10 /* 用于bin统计 */ 11 malloc_bin_stats_t stats; 12 };
即:在jemalloc的内存管理层面存在如下关系:
在代码的调用上我们以定义了一个bin的概念,仅用来区分不同size的内存块(region),其实bins就是一个固定长度的数组,不同的index代表不同的内存大小档位,
bins
比如,bin[0]大小为8字节,则bin[0]会关联多个8字节的runs,记录当前在哪个run上,如果当前run用完了,可以找到下一个run继续分配内存;如果当前所有的run都用完了,则申请一个chunk,分配出所需要空间,剩余的交给arena->runs_avail管理。
推荐
http://tinylab.org/memory-allocation-mystery-%C2%B7-jemalloc-a/
二、jemalloc利用malloc的hook来对代码中的malloc进行替换
JEMALLOC_EXPORT void (*__free_hook)(void *ptr) = je_free;
JEMALLOC_EXPORT void *(*__malloc_hook)(size_t size) = je_malloc;
JEMALLOC_EXPORT void *(*__realloc_hook)(void *ptr, size_t size) = je_realloc;
这样我们在自己的程序中使用malloc的时候就会被替换成je_malloc了;
三、mallctl的使用
# define mallctl je_mallctl
je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,
size_t newlen) ;
此函数可以用来查询也可以用来设置;
1,查询:
则指定要查询的名称name,及结果返回的指针oldp,还有返回指针的长度oldlenp;
eg:
const char* j;
size_t s = sizeof(j);
mallctl("version", &j,&s,NULL,0);
std::cout<<j<<"
";
2,设置:
则指定要设置的名称name,要设置变量的地址newp,要设置变量的长度newlen
eg:
bool active = true;
mallctl("prof.active", NULL, NULL,(void*)&active, sizeof(active));