C实现的内存chunk管理器

C语言实现的内存块管理器
这是一个C语言实现的内存chunk管理器,具备可扩展性,支持Top、Push、Pop操作。内存chunk可根据需要有不同的大小,适用于创建简单的内存管理系统,优化内存分配和释放。作为双向队列的内部容器,其在存储大量简单对象时,相比双向链表具有更高的效率和内存利用率。

功能:


1.C实现的简单的,可扩展的内存chunk管理器;

2.支持Top, Push, Pop几种简单的操作;

3.由于一个内存chunk可以有多个元素,不同的chunk可以大小不等;


优点:


1.可以将其扩展为一个简单的内存管理器,实现分配和释放的cache机制;

2.可以将其作为双向队列的内部的容器类;

3.用这个chunk存储大量的简单对象(支持FIFO)效率远比用双向链表高(速度),同时更节约内存(双向链表一个node,需要两个指针维护);


代码如下,compile and test in visual studio 2005:

#ifndef _CHUNK_LIST_H_
#define _CHUNK_LIST_H_

#define INIT_ELEM_COUNT 20

typedef struct tagChunk
{
	size_t cur;
	size_t end;
	tagChunk*  next;
	tagChunk*  prev;

	char   buf[0];

}Chunk, *pChunk;


void  InitChunk( pChunk* newChk, size_t elemSize, size_t elemCount )
{
	pChunk chk = *newChk;
	chk = (pChunk)malloc(elemCount * elemSize + sizeof(Chunk));
	if( chk )
	{
		chk->cur = 0;
		chk->end = elemSize * elemCount;
		chk->next = 0;
		chk->prev = 0;

		*newChk = chk;
	}


}

void* Push( pCh
<think>我们正在讨论Glibc堆内存管理中的bins和chunk。根据用户提供的引用和之前的问题,我们需要详细解释bins和chunk的工作原理和作用。 首先,chunk是堆内存管理的基本单位。每次使用malloc申请内存时,实际上得到的是一个chunkchunk的结构包括元数据和用户数据部分。在释放时,chunk会被放入相应的bin中,以便后续重用。 bins是用于管理空闲chunk的链表数组。根据chunk的大小和状态,bins分为多种类型:Fast bins, Unsorted bin, Small bins, Large bins。每种bin有不同的特性和用途。 下面我们详细展开: 1. Chunk结构: - 每个chunk内存中都有一个头(header)用于存储元数据,包括当前chunk的大小(size)和前一个chunk是否在使用中的标志(P标志)。当chunk被释放时,其用户数据区域会存储额外的信息,如指向链表中前后chunk的指针(因为此时用户数据区域空闲,可以复用)。 - chunk的大小必须是2*SIZE_SZ(在32位系统中是8字节,64位是16字节)的整数倍。 - chunk内存中连续存放,相邻的chunk可以通过当前chunk的地址和大小计算得到。 2. Bins分类: a) Fast bins: - 用于管理小尺寸(默认小于80字节)的空闲chunk。 - 采用单链表结构,每个bin中的chunk大小相同(有7个fast bin,每个bin中的chunk大小依次为16, 24, 32, ..., 80字节,在64位系统上为32, 48, 64, ..., 160字节)。 - 采用LIFO(后进先出)策略,即新释放的chunk会被插入到链表头部,分配时也从头部取。 - 为了速度,fast bins中的chunk在释放时不会合并(相邻的空闲chunk不会合并,这样可以快速分配,但可能导致碎片)。 b) Unsorted bin: - 只有一个,用于存放最近释放的chunk,不论大小(只要不是特别大,特别大的会单独处理)。 - 它是一个双向循环链表,采用FIFO(先进先出)策略。 - 设计unsorted bin的目的是为了快速重新利用最近释放的chunk,因为程序可能很快又要申请同样大小的内存。 - 在分配内存时,malloc会首先检查unsorted bin,如果找到合适大小的chunk,就直接返回;否则,就会把unsorted bin中的chunk放入相应的small bin或large bin中。 c) Small bins: - 有62个(在64位系统中是62个,32位是63个?实际上,通常有64个bin,其中前62个用于small bins,后63个用于large bins,加上一个unsorted bin和fast bins,但具体数量可能因版本而异)。每个small bin管理固定大小的chunk。 - 每个small bin中的chunk大小相同,相邻bin之间相差8字节(64位系统为16字节)。例如,第一个small bin存放16字节的chunk,第二个存放24字节的chunk,以此类推。 - 采用双向循环链表,FIFO策略。 - 当释放的chunk被放入small bin时,会与相邻的空闲chunk合并(如果相邻的也是空闲的)以减少碎片。 d) Large bins: - 用于管理大于等于1024字节(在64位系统中可能是1024字节,但具体阈值可能不同)的空闲chunk。 - 每个large bin中的chunk大小在一定范围内,并且bin中的chunk按大小排序(从大到小或从小到大)。 - 也是双向循环链表。 - 在分配内存时,需要遍历链表找到足够大的chunk,可能进行分割(取一部分,剩下的放入unsorted bin或相应的bin中)。 3. 工作流程: 当调用malloc申请内存时: - 首先,根据申请的大小,如果属于fast bin范围,则在对应的fast bin中查找,如果有则直接返回。 - 否则,如果申请的大小属于small bin范围,则查找对应的small bin,如果有则返回。 - 如果以上都没有,则遍历unsorted bin(同时会将其中的chunk根据大小放入small bin或large bin中),在遍历过程中如果找到合适大小的chunk,则返回。 - 如果还没有,则搜索large bin,找到最小能满足要求的chunk(可能分割,剩余部分放入unsorted bin)。 - 如果large bin中也没有,则尝试从top chunk(堆顶的空闲内存)中分割。 - 如果top chunk也不够,则通过系统调用(brk或mmap)扩展堆,形成新的top chunk,然后再分割。 当调用free释放内存时: - 如果释放的chunk大小在fast bin范围内,则放入相应的fast bin(不设置合并标志,所以不会合并相邻的空闲chunk)。 - 否则,如果释放的chunk不与top chunk相邻,则放入unsorted bin(同时会检查前后相邻的chunk是否空闲,如果空闲则合并成一个大的chunk,然后放入unsorted bin)。 - 如果释放的chunk与top chunk相邻,则直接合并到top chunk中,不放入任何bin。 4. 合并操作: 为了避免内存碎片,当释放的chunk不在fast bin范围内时,堆管理器会尝试合并相邻的空闲chunk。合并的条件是相邻的chunk也是空闲的(通过检查相邻chunk的P标志位,该标志位表示前一个chunk是否在使用中,以及通过当前chunk的size找到下一个chunk,并检查其空闲标志)。合并后的chunk会放入unsorted bin(除非与top chunk相邻,则直接合并到top chunk)。 5. 多线程竞争: 在多线程环境下,每个线程有自己的arena(分配区),每个arena有自己独立的bins和top chunk,这样可以减少锁的竞争。但是,当线程自己的arena不够时,会尝试访问主arena或其他arena,这时需要加锁。 6. 碎片问题: 内存碎片分为内部碎片和外部碎片。内部碎片是由于分配的内存块大于实际请求的大小(比如对齐要求)造成的。外部碎片是空闲内存分散,无法满足大块连续内存请求。通过合并相邻空闲chunk可以减少外部碎片,但fast bins中的chunk不合并可能导致碎片(但这样分配快,是一种权衡)。 7. 根据引用[1][3]:unsorted bin的设计是为了快速重用最近释放的chunk,并且会立即合并物理地址相邻的chunk(但fast bins中的chunk不会立即合并,只有在特定时机才会合并到unsorted bin中)。 8. 根据引用[2][4]:当释放的chunk不与top chunk相邻时,会形成碎片,并放入unsorted bin。在malloc时,会整理unsorted bin上的chunk到small bin或large bin上。 下面我们通过一个例子说明: 假设我们释放一个chunk(不在fast bin范围内): - 首先,检查前一个chunk是否空闲(通过当前chunk的P标志位,如果为0,则表示前一个chunk空闲),如果空闲,则合并。 - 然后,通过当前chunk的size找到下一个chunk,检查下一个chunk是否空闲(通过下一个chunk的size字段中的标志位),如果空闲,则合并。 - 合并后的大chunk会被放入unsorted bin。 在malloc时,如果请求的内存大小在small bin范围内,会先检查对应的small bin,如果没有,则遍历unsorted bin。在遍历unsorted bin时,如果遇到一个chunk的大小正好匹配,则直接返回;如果比请求的大,则分割(分割后剩余部分重新放入unsorted bin);如果比请求的小,则将其放入对应的small bin或large bin中,然后继续遍历。 这样设计的目的在于提高内存分配和释放的效率,同时减少碎片。 注意:以上描述基于ptmalloc2(glibc使用的堆管理器)的实现。 下面我们以代码块形式展示chunk的结构(伪代码): ```c struct malloc_chunk { size_t mchunk_prev_size; // 前一个chunk的大小(如果前一个chunk空闲) size_t mchunk_size; // 当前chunk的大小(包括头部,以及三个标志位:A, M, P) struct malloc_chunk* fd; // 空闲时使用:指向同一个bin中的下一个chunk(双向链表) struct malloc_chunk* bk; // 以下仅在非空闲时存在(用户数据区域),但为了空间复用,在空闲时,用户数据区域也可以被fd和bk覆盖 }; ``` 其中,size字段的最低三个比特位用于标志: - PREV_INUSE (P): 前一个chunk是否在使用中(1表示在使用,0表示空闲) - IS_MMAPPED (M): 当前chunk是否是通过mmap分配的 - NON_MAIN_ARENA (A): 当前chunk是否属于非主分配区 在释放时,chunk的fd和bk指针会被设置,用于链接到对应的bin中。 总结: - chunk内存分配的基本单位,包含头部信息和用户数据。 - bins是用于管理空闲chunk的链表,根据chunk的大小和特性分为fast bins, unsorted bin, small bins, large bins。 - 不同的bin采用不同的管理策略,以平衡分配速度和内存碎片。 相关问题: 1. 如何避免内存碎片?ptmalloc采用了哪些策略? 2. 在多线程环境下,ptmalloc如何减少锁竞争? 3. 什么情况下会使用mmap分配内存?与brk分配的内存有何区别? 4. 为什么fast bins中的chunk不立即合并? 5. 在释放内存时,如何合并相邻的chunk?具体步骤是什么?</think>### Glibc堆内存管理中的bins和chunk详解 #### 一、Chunk内存管理的基本单元 **Chunk是堆内存分配的最小单位**,每个内存块都由chunk结构管理。其内存布局如下: ```c struct malloc_chunk { size_t mchunk_prev_size; // 前一个chunk的大小(若前chunk空闲) size_t mchunk_size; // 当前chunk大小 + 标志位 struct malloc_chunk* fd; // 仅空闲时有效:指向同bin的下一个chunk struct malloc_chunk* bk; // 仅空闲时有效:指向同bin的上一个chunk }; ``` **关键特性**: 1. **大小对齐**:chunk大小总是$2 \times \texttt{sizeof(size\_t)}$的倍数(32位系统8字节,64位16字节) 2. **标志位**:`mchunk_size`的低3位用作标志: - `A (0x4)`: 是否属于非主分配区 - `M (0x2)`: 是否mmap分配 - `P (0x1)`: 前一个chunk是否在使用中 3. **空间复用**:用户数据区在chunk空闲时存储链表指针 4. **边界标记**:通过`mchunk_prev_size`实现相邻chunk的快速合并[^2] #### 二、Bins:空闲chunk的管理容器 Bins是用于组织空闲chunk的链表数组,根据chunk大小和管理策略分为四类: ##### 1. Fast Bins - **管理范围**:小尺寸chunk(默认<80字节) - **数据结构**:单链表(LIFO) - **行为特征**: - 不合并相邻chunk(避免碎片化开销) - 分配/释放操作无锁(单线程优化) - **工作流程**: ```mermaid graph LR A[释放小chunk] --> B{加入Fast Bin} C[分配小内存] --> D{从Fast Bin取chunk} ``` ##### 2. Unsorted Bin - **核心作用**:释放chunk的临时缓冲区 - **数据结构**:双向循环链表(FIFO) - **关键机制**: 1. 接收所有非fast bin的释放chunk 2. 立即合并物理相邻的空闲chunk[^3] 3. 作为malloc搜索的第一站 - **工作流程**: ```mermaid graph TD A[malloc请求] --> B{搜索Unsorted Bin} B -- 匹配 --> C[直接分配] B -- 不匹配 --> D[移入Small/Large Bin] ``` ##### 3. Small Bins - **管理范围**:中等尺寸chunk(512B~1024B) - **数据结构**:62个双向循环链表 - **特性**: - 每个bin管理固定大小的chunk(等差排列) - 严格FIFO顺序 - 分配时触发相邻chunk合并 ##### 4. Large Bins - **管理范围**:大尺寸chunk(≥1024B) - **数据结构**:63个双向循环链表 - **特性**: - 每个bin管理大小范围(如1024-1088B) - chunk按大小降序排序 - 分配时采用"最佳匹配"策略 #### 三、核心工作流程 ##### 内存分配(malloc) ```mermaid graph TD A[malloc请求] --> B{大小≤max_fast?} B -- 是 --> C[Fast Bins搜索] B -- 否 --> D{大小≤512B?} D -- 是 --> E[Small Bins搜索] D -- 否 --> F[Large Bins搜索] C/E/F -- 未找到 --> G[Unsorted Bin搜索] G -- 未找到 --> H[分割Top Chunk] H -- 失败 --> I[brk/mmap扩展堆] ``` ##### 内存释放(free) ```mermaid graph TD A[free操作] --> B{chunk大小≤max_fast?} B -- 是 --> C[加入Fast Bin] B -- 否 --> D{与Top Chunk相邻?} D -- 是 --> E[合并到Top Chunk] D -- 否 --> F[加入Unsorted Bin] F --> G[检查相邻chunk] G -- 发现空闲 --> H[合并后加入Unsorted Bin] ``` #### 四、关键机制解析 1. **Chunk合并**: - 触发条件:非fast bin释放时 - 合并方式: - 检查前一个chunk(通过`P`标志位) - 检查后一个chunk(通过chunk大小计算地址) - 数学表达:$C_{\text{merged}} = C_{\text{prev}} + C_{\text{current}} + C_{\text{next}}$ 2. **Bin维护策略**: - Unsorted Bin整理时机: - malloc遍历Unsorted Bin时 - 空闲chunk超过阈值(默认64KB) - 整理操作: ```c while(unsorted_bin非空){ 取出chunk; if(chunk大小匹配请求) 直接分配; else if(属于small范围) 插入Small Bin; else 按大小插入Large Bin; } ``` 3. **碎片优化**: - Fast Bin延迟合并:平衡分配速度与碎片 - Top Chunk机制:保留连续内存用于扩展 - 最佳适应策略:Large Bin减少大内存浪费 #### 五、实际案例解析 **场景**:释放非相邻chunk形成碎片 ```c void* a = malloc(512); // chunkA [0x1000, 0x1200] void* b = malloc(1024); // chunkB [0x1200, 0x1600] free(a); // chunkA进入Unsorted Bin ``` 此时内存布局: ``` 0x1000: chunkA (空闲, 在Unsorted Bin) 0x1200: chunkB (使用中) 0x1600: top chunk ``` 由于chunkA不与top chunk相邻,无法合并,形成内存碎片[^2]。当后续分配请求: - 512B请求:直接从Unsorted Bin分配chunkA - 600B请求:需要分割top chunk #### 六、多线程优化 1. **Arena机制**: - 主线程使用`main_arena` - 新线程创建`thread_arena` - 每个arena独立管理bins 2. **锁策略**: - Fast Bin操作无锁(单链表) - Unsorted Bin操作需加锁 - 采用原子操作减少锁竞争 --- ### 相关问题 1. **如何通过chunk边界标记实现快速合并?合并操作的数学原理是什么?** 2. **在什么场景下Fast Bin的延迟合并会导致严重内存碎片?如何检测这类问题?** 3. **多线程环境下arena的分配策略如何平衡性能与内存开销?** 4. **Large Bin的最佳匹配算法具体如何实现?时间复杂度是多少?** 5. **如何利用ptmalloc的内部机制设计高效的内存池?** [^1]: Unsorted Bin作为缓冲区加速内存分配 [^2]: 非相邻释放导致内存碎片 [^3]: Unsorted Bin立即合并相邻chunk [^4]: Unsorted Bin整理机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值