为何指针数组调用free()无法释放内存?附代码执行异常现象
分析你的指针数组内存释放异常问题
嘿,我来帮你拆解下这段代码里导致系统监视器数值异常的原因,以及背后的内存管理逻辑:
首先先把你的代码放在代码块里方便参考:
int ** t = (int**) malloc(sizeof(int*)*10000000); printf("1\n"); getchar(); for(int i =0; i < 10000000; i++){ t[i] = (int*) malloc(sizeof(int)); *t[i] = i; } printf("2\n"); getchar(); for(int i =0; i < 10000000; i++){ free(t[i]); } printf("3\n"); getchar(); free(t); printf("4\n"); getchar();
各个阶段内存变化的核心原因
你看到的系统监视器异常,本质是用户态内存分配器的行为和操作系统内存统计的逻辑不匹配,逐个阶段给你解释:
- 输出
1时:你只分配了一个存放1000万个指针的数组,在64位系统里这个数组大小是10,000,000 * 8字节 = 80MB,这时候内存占用应该是正常的,系统监视器数值不会太夸张。 - 输出
2时:你给每个指针都分配了一个单独的int内存块——这里的问题是,每个malloc出来的小内存块,除了4字节的int数据,还会附带内存分配器的元数据(比如块大小、前后块指针等)。1000万个这样的小块,元数据的总开销可能比实际数据还大,再加上内存分配器为了减少碎片做的对齐、分组,实际占用的内存会远大于你计算的80MB + 40MB = 120MB,这时候系统监视器里的内存飙升是正常的,但数值看起来“异常”是因为你没算上分配器的额外开销。 - 输出
3时:你逐个free了每个int块,但这些释放的小内存不会立刻还给操作系统。像glibc的ptmalloc这类常用分配器,会把空闲的小内存块放到自己的内存池里,留着后续程序复用,不会立刻归还给系统。所以这时候系统监视器显示的“已用内存”不会立刻降到输出1时的水平,这是很多人误以为内存泄漏的常见误区。 - 输出
4时:free(t)释放了指针数组本身,但同样,内存分配器是否把这块大内存还给系统,取决于它的策略。如果内存池里还有很多细碎的空闲块,系统监视器的数值还是不会立刻回落,直到分配器判断这些空闲内存不会再被复用,才会批量归还给操作系统。
优化建议
如果你想避免这种“异常”的内存数值,同时提升内存使用效率,可以试试这些方法:
- 避免海量小内存分配:改成用连续的一维数组模拟,只需要一次分配和一次释放:
这样没有额外的元数据开销,内存占用完全可控,也不会产生大量内存碎片。int *t = malloc(sizeof(int) * 10000000); // 使用时直接 t[i] = i; free(t); - 用内存池管理小内存:如果必须用指针数组的形式,可以提前分配一块大内存,自己切割成小块分给每个
t[i],减少分配器的开销。 - 用专业工具检测泄漏:不要只看系统监视器判断内存泄漏,用
valgrind这类工具可以准确检测是否有未释放的内存,它会告诉你真正的泄漏点,而不是被分配器的内存池误导。
内容的提问来源于stack exchange,提问作者dulef123
相关产品推荐
相关产品推荐

