------------------------------------------------------------
author: hjjdebug
date: 2024年 07月 25日 星期四 11:32:19 CST
decription: free() 从哪里知道释放的内存大小.
------------------------------------------------------------
malloc()函数申请的一块内存,用完后我们会调用free()函数释放内存,
而free()函数只传入了一个地址,怎么能知道要释放多大的内存呢?
实际上系统为每块分配的内存加了一个16字节的头部信息. 并把最终的大小按16字节对齐
例如:
申请1字节内存: 分配16+1 按16对齐为32 bytes, 计算 (17+15)/16*16=32
申请8字节内存: 分配16+8 按16对齐为32 bytes, 计算 (24+15)/16*16=32
申请16字节内存: 分配16+16 按16对齐为32 bytes, 计算 (32+15)/16*16=32
申请32字节内存: 分配16+32 按16对齐为48 bytes, 计算 (48+15)/16*16=48
申请33字节内存: 分配16+33 按16对齐为64 bytes, 计算 (49+15)/16*16=64
申请64字节内存: 分配16+64 按16对齐为80 bytes, 计算 (80+15)/16*16=80
下面给出试验程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{ //头部字节占16个bytes
char *buf1 = (char*)malloc(1); //申请1字节内存, 也会占用32个byte, 因为它们按16字节对齐
char *buf2 = (char*)malloc(8); //申请8字节内存
char *buf3 = (char*)malloc(16); //申请16字节内存, 占32个bytes
char *buf4 = (char*)malloc(32); //申请32字节内存, 占用48个bytes
char *buf5 = (char*)malloc(64); //申请64字节内存, 占用80个bytes
memset(buf1, 0xaa, 1); //填充字节,以便方便观察
memset(buf2, 0xbb, 8);
memset(buf3, 0xcc, 16);
memset(buf4, 0xdd, 32);
memset(buf5, 0xee, 64);
printf("buf1:%p\n",buf1);
printf("buf2:%p\n",buf2);
printf("buf3:%p\n",buf3);
printf("buf4:%p\n",buf4);
printf("buf5:%p\n",buf5);
free(buf1); //free 从头字节中可以得到分配的内存大小(按16bytes 对齐,后4bits有其它属性意义)
free(buf2);
free(buf3);
free(buf4);
free(buf5);
// free(buf1); //第二次释放?! 会崩溃! 已经释放的内存再次释放系统会不干的!
return 0;
}
下面给出gdb 调试观察内存分配的结果. 是在x86_64 cpu上运行的结果
问题1: 头部16字节只代表长度, 为什么长度总是有一个1呢?
答: 长度是16字节对齐的,所以后面4个bits 可以用作其它用途, bit0的1,只表示曾经分配过内存.
我跟踪了一下,释放内存后头部的字节没有变化,
所以这个bit0的1意义不详. 也可以认为每啥用途. 因为释放前后它都是1.
问题2: 对于64位系统, 如果表示长度,用8个字节就够了,为什么要保留16个字节呢?
答: 是的, 8个字节就够了,还留了8个, 全填为0, 是为了将来的扩展吧,反正目前没啥用途.
小结:
实际上系统为每块分配的内存加了一个16字节的头部信息. 并把最终的大小按16字节对齐
长度用8字节就够了,但系统保留了16字节
长度中bit0的1 其是没什么意义.
由于有以上过程,所以你申请的内存大小并不完全等于头部记录的大小, 记录的总比你申请的要大一点点.
所以你就不用斤斤计较为什么我申请的内存大小与头部记录的大小不是完全想等的了.