Kmalloc 共享内存池简介
在不同应用场景中,内存占用大小和申请释放频率各有不同。因此,需要根据实际情况对内存进行分类管理,从而提升内存管理的效率。但内存池在使用一段时间后往往会出现内存碎片,导致即使空闲内存很多,却无法申请大块内存,造成内存利用率降低。
在某些业务场景中,还需要跨进程进行数据共享。同时在内存池的使用过程中,我们需要了解内存池的使用情况,检测内存泄漏、重复释放、内存越界等异常情况。Kmalloc 是开务数据库自研的一款共享内存池,其重点采用了以下攻克方式:
-
为了满足不同类型内存的使用,我们将内存池分为多个 Heap,每个 Heap 使用不同的结构管理不同类型的内存。
-
为了提高内存利用率,我们在申请和释放内存时做了一些额外的优化处理。
-
为了实现高速的数据共享,我们允许多个进程访问同一块内存,并使用并发访问控制来管理内存的释放。
-
为了监控内存池使用情况,我们提供了一些内存池 Stats 接口和 Debug 模式。
Kmalloc 共享内存池的架构
1、内存池区域划分
Kmalloc 共享内存池由 Header Heap、Free Heap、Common Heap、TS Data Heap、System Heap 组成,初始化时先按照预设参数大小向操作系统申请并固定共享内存,而后完成各个 Heap 的初始化。

在共享内存头部区域固定下的内存作为 Header Heap,用来存放产品其他模块和 Kmalloc 的 Header;剩余的内存全部用来初始化 Free Heap;其他 3 个 Heap 向 Free Heap 申请内存。
2、内存分类管理
内存按照使用场景,可分成不同的 MemType,由不同的 Heap 进行管理:

内存按照大小分别由不同的数据结构进行管理:

-
Thread Cache 和 CentralCache由若干条 Freelist 构成,每条 Freelist 由相同大小的内存块采用嵌入式指针连接而成。
-
PageHeap 也由若干 Freelist 构成,区别在于 Freelist 中的元素为 Span 结构体。
-
SkipList 则是一条 Span 按照内存大小排序的跳表。
大块连续的内存按照 Page(8K) 为最小单位进行追踪,一个或多个连续的 Page 构成一个 Span,由 Span 结构体记录它的 Page 范围、MemType 等详细信息。Page ID 与 Span 的映射关系由 Page Map(通过 Radix-Tree 实现,如下图)进行维护。

3、内存池区域****结构
除 Header Heap 外,每个 Heap 根据其管理内存的特点,分别由不同的数据结构组成。Common Heap 的结构及内存申请释放流程如下。

其他 3 个 Heap 都是 Common Heap 的简化:TSData Heap 由 Thread Cache 和 Central Cache 构成,System Heap 只包含 Central Cache,Free Heap 只包含 SkipList。
Kmalloc 共享内存池如何提高内存利用率
1、使用嵌入式指针连接 Freelist 中的元素。
2、内存对齐:
-
小内存映射到一个对齐的大小类型作为实际申请的内存大小,最低 8 字节对齐,且随着内存大小的增长,对齐的单位也相应地增大。
-
中大内存以 Page 为单位向上取整。
3、避免申请小块内存:
-
Central Cache 向 Page Heap 申请内存时,最小单位是 Page。
-
各个 Heap 向 Free Heap 申请内存时,如果实际需要的内存不满 1 M,先尝试申请 1M 内存;如果申请失败,再尝试申请实际需要的内存大小。
4、内存 GC 算法:
Thread Cache 和 Central Cache 采用 GC 算法使闲置的内存快速释放回上一层。Page Heap 和 SkipList 收到下层释放的内存后,会尽量将相邻的空闲内存合。
Kmalloc 共享内存的并发访问管理
**使用场景:**进程 A 从共享内存池申请了一块内存,共享给进程B。具体流程如下:
-
进程 A 调用注册共享内存的接口,将进程 ID 记录到引用计数器中,并得到相应的内存偏移量。
-
使用消息队列将偏移量传送给进程 B。
-
进程 B 收到偏移量后,调用访问共享内存的接口,将其转换成具体的内存地址,并将进程 ID 记录到引用计数器中。然后通过类型转换,即可访问该内存。
-
两个进程在使用完共享内存后,都需要调用 Free 释放对内存的引用,当引用为 0 时真正释放内存。
-
如果进程 B 未释放内存引用就 Crash,进程 A 可以调用内存引用释放接口,主动释放进程 B 的内存引用。
Kmalloc 监控与异常检测
1、内存使用情况监控
-
提供查询内存池使用情况的接口,查询内存池当前使用内存的总大小和历史峰值、Free Heap剩余的内存大小等信息。
-
Free Heap 内存预警,当 Free Heap 剩余内存大小不足 80% 时,将输出预警信息。
2、Debug 模式
通过在分配的内存首尾添加 Debug Header、Debug Mark 和 Debug Tail,分别检测内存泄漏、重复释放、越界访问等问题。





