malloc 分配内存的具体细节

malloc 分配内存的具体细节

http://gengyanhui111.blog.163.com/blog/static/1401502102010111544840916/

 

C++ 2010-12-15 16:48:40 阅读9 评论0  字号: 订阅

了解 C 的内存分配,就要知道,C 程序是从 OS 中请求内存的,以 Windows 为例,CRT 调用 WIN API HeapAlloc 来请求堆内存分配的,HeapAlloc 需要一个句柄,指向进程的堆,这是 OS 管理内存用的,扯远了。 

好了,现在 CRT 已经有了堆内存了,那么应用程序要在堆上请求内存怎么做呢?不用我说吧?用 malloc,而 malloc 其实调来调去,其实是调用 _heap_alloc_dbg,_heap_alloc_dbg 接受一个参数 size_t nSize 用来表示程序要分配的内存大小,以 malloc(100) 为例,此参数也就是 100,但 _heap_alloc_dbg 不是请求分配 100 字节的内存,CRT 需要维护一个_CrtMemBlockHeader 的链表,_CrtMemBlockHeader 有一个成员 nDataSize 来表示用户请求分配的内存大小,_heap_alloc_dbg 实际上就是通过 HeapAlloc 来请求分配内存的,分配大小为 sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize,即 _CrtMemBlockHeader 结构的大小(32字节)加上用户请求的大小(100),再加上 4 字节的标志。 

好了,现在已经分配了足够的内存,难道就把所有的内存都给应用程序使用吗?当然不是,CRT 得到系统分配的内存的首地址 pHead,而给应用程序的内存实际上就是 pHead 偏移 32 字节(_CrtMemBlockHeader 的大小)所指向的地址,_heap_alloc_dbg 返回的就是这个地址。 


应用程序使用完内存之后,调用 free 函数,free 函数实际上调用的是 _free_dbg 函数,再调用_free_dbg_nolock 函数,_free_dbg_nolock 函数的第一个参数为 pUserData,即应用程序使用的内存地址,也就是 _heap_alloc_dbg 地址,也是 malloc 返回的地址,pUserData 减去 32 字节(_CrtMemBlockHeader 的大小)就是系统分配内存的首地址 pHead,通过 pHead 就可以得到一个_CrtMemBlockHeader 结构,这个结构的 nDataSize 成员就是应用程序请求的内存大小,以 malloc(100) 为例,此成员也就是 100,这样就可以计算出系统分配了多少内存,sizeof(_CrtMemBlockHeader) + 

nDataSize + nNoMansLandSize,即 32 + 100 + 4,为 136,所有可以正确释放分配的内存。 

### 自定义 `mymalloc` 函数实现内存分配过程 为了理解自定义的 `mymalloc` 如何工作,可以考虑其基本原理和具体实现细节。 #### 原理概述 `mymalloc` 的核心在于管理和分配堆中的连续内存块。通常情况下,该函数会请求操作系统提供一大片未使用的内存区域,并将其划分为更小的部分来满足不同的分配需求[^2]。 当首次调用 `mymalloc` 时,它可能通过系统调用来获取一块较大的原始内存池;之后每次被调用,则从这个预取的大块里切割出合适大小的小片段返回给用户程序使用。如果当前剩余的空间不足以满足新的申请量,则再次向OS索要更多资源扩充自己的可用范围。 #### 数据结构设计 对于内部管理已分配与空闲节点的信息流,一种常见做法是采用链表形式的数据结构: - **Free List**: 记录所有尚未被占用的碎片位置及其长度。 - **Allocated Blocks**: 维护着每一个成功分发出去的对象地址以及它们各自的尺寸信息。 这种机制允许快速查找合适的空白区间并执行分割操作,同时也便于后续回收再利用这些曾经释放过的部分。 #### 关键算法逻辑 以下是简化版的伪代码表示法展示了一个简单的 `mymalloc` 实现方式: ```c #include <stddef.h> #include <string.h> typedef struct Block { size_t size; struct Block* next; } Block; static char heap[HEAP_SIZE]; /* 预先设定好的固定大小 */ static Block head = { HEAP_SIZE, NULL }; void* mymalloc(size_t requestSize) { Block* currentBlock = &head; while (currentBlock != NULL && !(currentBlock->size >= requestSize)) { currentBlock = currentBlock->next; } if (!currentBlock || !requestSize) return NULL; void* allocatedMemoryAddress = ((char*)currentBlock + sizeof(Block)); // 如果找到足够大的区块则进行拆分处理 if(currentBlock->size > requestSize + sizeof(Block)){ Block* newBlock = (Block*)((char*)allocatedMemoryAddress + requestSize); newBlock->size = currentBlock->size - requestSize - sizeof(Block); newBlock->next = currentBlock->next; currentBlock->next = newBlock; currentBlock->size = requestSize; } // 将此block标记为已分配状态(这里简单起见不做额外字段) return allocatedMemoryAddress; } ``` 上述代码展示了如何遍历自由列表寻找适当大小的内存区段,并对其进行必要的调整以适应新来的请求。值得注意的是,在实际应用中还需要加入边界情况判断、错误检测等功能模块确保稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值