Linux内存管理(四):Jemalloc

1. Jemalloc简介

2. 基础知识

3. 内存分配(malloc)

4. 内存释放(free)

5. 内存再分配(realloc)

6. 内存回收(GC)

7. 优缺点


1. Jemalloc简介

    jemalloc 是由 Jason Evans 在 FreeBSD 项目中引入的新一代内存分配器。它是一个通用的 malloc 实现,侧重于减少内存碎片和提升高并发场景下内存的分配效率,其目标是能够替代 malloc。jemalloc 在 2005 年首次作为 FreeBSD libc 分配器使用,2010年,jemalloc 的功能延伸到如堆分析和监控/调优等。现代的 jemalloc 版本依然集成在 FreeBSD 中。

    jemalloc 应用十分广泛,在 Firefox、Redis、Rust、Netty 等出名的产品或者编程语言中都有大量使用。具体细节可以参考 Jason Evans 发表的论文 《A Scalable Concurrent malloc Implementation for FreeBSD》。这篇文章介绍 JeMalloc-5.1.0 版本。

2. 基础知识

    下图是旧版本的jemalloc:

    旧版本的jemalloc中, 每个arena内都会包含对应的管理信息,记录该arena的分配情况。arena都有专属的chunks, 每个chunk的头部都记录了chunk的分配信息。在使用某一个chunk的时候,会把它分割成很多个run,并记录到bin中。不同size的class对应着不同的bin,在bin里,都会有一个红黑树来维护空闲的run,并且在run里,使用了bitmap来记录了分配状态。此外,每个arena里面维护一组按地址排列的可获得的run的红黑树。 

    对于jemalloc 5.1.0版本来说,有几点需要说明:

  • chunk 这一概念被替换成了 extent;
  • dirty page 的 decay(或者说 gc) 变成了两阶段,dirty -> muzzy -> retained;
  • huge class 这一概念不再存在;
  • 红黑树不再使用,取而代之的是 pairing heap;

2.1 size_class

    每个 size_class 代表 jemalloc 分配的内存大小,共有 NSIZES(232)个小类(如果用户申请的大小位于两个小类之间,会取较大的,比如申请14字节,位于8和16字节之间,按16字节分配),分为2大类:

  • small_class小内存 :

    对于64位机器来说,通常区间是 [8, 14kb],常见的有 8, 16, 32, 48, 64, …, 2kb, 4kb, 8kb,注意为了减少内存碎片并不都是2的次幂,比如如果没有48字节,那当申请33字节时,分配64字节显然会造成约50%的内存碎片

  • large_class大内存:

    对于64位机器来说,通常区间是 [16kB, 7EiB],从 4 * page_size 开始,常见的比如 16kB, 32kB, …, 1mB, 2mB, 4mB,最大是 $2^{62}+3^{60}$,对于64位操作系统,页大小为4kB

  • size_index (size 索引:

    size 位于 size_class 中的索引号,区间为 [0,231],比如8字节则为0,14字节(按16计算)为1,4kb字节为28,当 size 是 small_class 时,size_index 也称作 binind

2.2 arena

    arena 是 jemalloc 最重要的部分,内存由一定数量的 arenas 负责管理。每个用户线程都会被绑定到一个 arena 上,线程采用 round-robin 轮询的方式选择可用的 arena 进行内存分配,为了减少线程之间的锁竞争,默认每个 CPU 会分配 4 个 arena,各个 arena 所管理的内存相互独立。

struct arena_s {

	atomic_u_t		nthreads[2];
	tsdn_t		*last_thd;

	arena_stats_t		stats;  // arena的状态

	ql_head(tcache_t)	tcache_ql;
	ql_head(cache_bin_array_descriptor_t)	cache_bin_array_descriptor_ql;
	malloc_mutex_t				tcache_ql_mtx;

	prof_accum_t		prof_accum;
	uint64_t		prof_accumbytes;

	atomic_zu_t		offset_state;

	atomic_zu_t		extent_sn_next;  // extent的序列号生成器状态

	atomic_u_t		dss_prec;   

	atomic_zu_t		nactive;    // 激活的extents的page数量

	extent_list_t	large;      // 存放 large extent 的 extents

	malloc_mutex_t	large_mtx;  // large extent的锁

	extents_t extents_dirty;    // 刚被释放后空闲 extent 位于的地方

	extents_t extents_muzzy;    // extents_dirty 进行 lazy purge 后位于的地方,dirty -> muzzy

	extents_t extents_retained; // extents_muzzy 进行 decommit 或 force purge 后 extent 位于的地方,muzzy -> retained

	arena_decay_t	decay_dirty; // dirty --> muzzy 

	arena_decay_t	decay_muzzy; // muzzy --> retained 

	pszind_t		extent_grow_next;
	pszind_t		retain_grow_limit;
	malloc_mutex_t		extent_grow_mtx;

	extent_tree_t		extent_avail;     // heap,存放可用的 extent 元数据

	malloc_mutex_t		extent_avail_mtx; // extent_avail的锁

	bin_t			bins[NBINS];      // 所有用于分配小内存的 bin

	base_t			*base;            // 用于分配元数据的 base

	nstime_t		create_time;      // 创建时间
};

   几种extent的状态如下:

内存状态 备注
clean 分配给用户或 tcache
dirty 用户调用 free 或 tcache 进行了 gc
muzzy extents_dirty 对 extent 进行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值