Ubuntu下C程序运行时RES内存free后未回落问题求助
Ubuntu下程序因top中RES内存持续增长崩溃,编写测试程序跟踪malloc/free对应的RES内存,但结果不符合预期:
测试代码
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <unistd.h> #include <sys/resource.h> #define CAPACITY_DEBUG 1000000 void printMemUsage(){ struct rusage usage; getrusage(RUSAGE_SELF, &usage); fprintf(stderr,"Memory usage: %ld KB .................\n", usage.ru_maxrss); } void MallocTest(){ fprintf(stderr,"test 1 started ......................!!\n"); printMemUsage(); sleep(5); fprintf(stderr,"starting to allocate ....................\n"); int* arr = (int*)malloc(CAPACITY_DEBUG * sizeof(int)); if (arr == NULL) { fprintf(stderr, "Memory allocation failed. Exiting...\n"); return; } for(int i = 0; i < CAPACITY_DEBUG; i++){ arr[i] = 5; } printMemUsage(); fprintf(stderr,"allocation done, going to sleep................\n"); sleep(15); fprintf(stderr,"Woke up, starting to free............\n"); free(arr); // Free the memory when you're done. // malloc_trim(0); fprintf(stderr,"free done................\n"); sleep(5); printMemUsage(); } void MallocTest2(){ fprintf(stderr,"test 2 started ......................!!\n"); printMemUsage(); sleep(5); fprintf(stderr,"starting to allocate ....................\n"); int* arr[CAPACITY_DEBUG]; // Declare an array of integer pointers for (int i = 0; i < CAPACITY_DEBUG; i++) { arr[i] = (int*)malloc(sizeof(int)); // Allocate memory for each element if (arr[i] == NULL) { // Handle memory allocation failure perror("malloc"); exit(1); } // Initialize the value in the allocated memory *arr[i] = i * 2; } printMemUsage(); fprintf(stderr,"allocation done, going to sleep................\n"); sleep(15); fprintf(stderr,"Woke up, starting to free............\n"); // Don't forget to free the allocated memory when done for (int i = 0; i < CAPACITY_DEBUG; i++) { free(arr[i]); } //malloc_trim(0); fprintf(stderr,"free done................\n"); sleep(30); printMemUsage(); } int main(){ MallocTest2(); return 1; }
程序运行输出
$ ./memTest2 test 2 started ......................!! Memory usage: 2680 KB ................. starting to allocate .................... Memory usage: 40560 KB ................. allocation done, going to sleep................ Woke up, starting to free............ free done................ Memory usage: 40560 KB .................
Valgrind运行输出
$ valgrind --leak-check=full --show-leak-kinds=all ./memTest2 ==104110== Memcheck, a memory error detector ==104110== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==104110== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==104110== Command: ./memTest2 ==104110== ### unhandled dwarf2 abbrev form code 0x25 ### unhandled dwarf2 abbrev form code 0x25 ### unhandled dwarf2 abbrev form code 0x25 ### unhandled dwarf2 abbrev form code 0x1b get_Form_szB: unhandled 27 (DW_FORM_addrx) --104110-- WARNING: Serious error when reading debug info --104110-- When reading debug info from /home/reve/reve_dpdk_v22.11.1_ubuntu_v22.04.2/projects/fuji_projects/dpdkbasic/memTest2: --104110-- get_Form_contents: unhandled DW_FORM ==104110== Warning: client switching stacks? SP change: 0x1fff0002b8 --> 0x1ffe85f020 ==104110== to suppress, use: --max-stackframe=8000152 or greater ==104110== Invalid write of size 8 ==104110== at 0x109345: MallocTest2 (src/media_queue_handler/queueTest.c:83) ==104110== by 0x10BDE7: ??? (in /home/reve/reve_dpdk_v22.11.1_ubuntu_v22.04.2/projects/fuji_projects/dpdkbasic/memTest2) ==104110== by 0x1094B5: main (src/media_queue_handler/queueTest.c:116) ==104110== Address 0x1ffe85f018 is on thread 1's stack ==104110== in frame #0, created by MallocTest2 (src/media_queue_handler/queueTest.c:82) ==104110== test 2 started ......................!! ==104110== Invalid read of size 8 ==104110== at 0x48E8026: fwrite (iofwrite.c:49) ==104110== by 0x109349: MallocTest2 (src/media_queue_handler/queueTest.c:83) ==104110== by 0x1094B5: main (src/media_queue_handler/queueTest.c:116) ==104110== Address 0x1ffe85f018 is on thread 1's stack ==104110== in frame #0, created by fwrite (iofwrite.c:31) ==104110== ==104110== Syscall param getrusage(usage) points to unaddressable byte(s) ==104110== at 0x498254B: getrusage (getrusage.c:29) ==104110== by 0x10935A: MallocTest2 (src/media_queue_handler/queueTest.c:17) ==104110== by 0x1094B5: main (src/media_queue_handler/queueTest.c:116) ==104110== Address 0x1ffe85f0b0 is on thread 1's stack ==104110== in frame #1, created by MallocTest2 (src/media_queue_handler/queueTest.c:82) ==104110== Memory usage: 55336 KB ................. starting to allocate .................... ==104110== Invalid write of size 8 ==104110== at 0x1093AA: MallocTest2 (src/media_queue_handler/queueTest.c:90) ==104110== by 0x1094B5: main (src/media_queue_handler/queueTest.c:116) ==104110== Address 0x1ffe85f140 is on thread 1's stack ==104110== in frame #0, created by MallocTest2 (src/media_queue_handler/queueTest.c:82) ==104110== ==104110== Syscall param getrusage(usage) points to unaddressable byte(s) ==104110== at 0x498254B: getrusage (getrusage.c:29) ==104110== by 0x1093D5: MallocTest2 (src/media_queue_handler/queueTest.c:17) ==104110== by 0x1094B5: main (src/media_queue_handler/queueTest.c:116) ==104110== Address 0x1ffe85f020 is on thread 1's stack ==104110== in frame #1, created by MallocTest2 (src/media_queue_handler/queueTest.c:82) ==104110== Memory usage: 204140 KB ................. allocation done, going to sleep................ Woke up, starting to free............ ==104110== Invalid read of size 8 ==104110== at 0x109430: MallocTest2 (src/media_queue_handler/queueTest.c:106) ==104110== by 0x1094B5: main (src/media_queue_handler/queueTest.c:116) ==104110== Address 0x1ffe85f140 is on thread 1's stack ==104110== in frame #0, created by MallocTest2 (src/media_queue_handler/queueTest.c:82) ==104110== free done................ Memory usage: 204140 KB ................. ==104110== Warning: client switching stacks? SP change: 0x1ffe85f020 --> 0x1fff0002b8 ==104110== to suppress, use: --max-stackframe=8000152 or greater ==104110== ==104110== HEAP SUMMARY: ==104110== in use at exit: 0 bytes in 0 blocks ==104110== total heap usage: 1,000,000 allocs, 1,000,000 frees, 4,000,000 bytes allocated ==104110== ==104110== All heap blocks were freed -- no leaks are possible ==104110== ==104110== For lists of detected and suppressed errors, rerun with -s ==104110== ERROR SUMMARY: 1999938 errors from 6 contexts (suppressed: 0 from 0)
预期free后内存回落,但实际内存用量远大于分配量且未回落,Valgrind显示无泄漏但有大量错误,求问题原因及正确跟踪方法。
1. 内存统计函数用错了,根本没拿到当前RES值
你使用的ru_maxrss是进程启动以来的最大驻留内存峰值,不是当前的RES值!所以不管怎么free,这个值只会上升不会下降,这是你看到free后内存数值不变的核心原因。
要获取当前的驻留内存(对应top的RES),应该读取/proc/self/status中的VmRSS字段,或者/proc/self/statm的第二个数值。修改printMemUsage函数:
#include <string.h> void printMemUsage(){ FILE* fp = fopen("/proc/self/status", "r"); if (!fp) return; char line[256]; while (fgets(line, sizeof(line), fp)) { if (strncmp(line, "VmRSS:", 6) == 0) { fprintf(stderr, "Current RES: %s", line + 6); break; } } fclose(fp); }
2. MallocTest2存在严重的栈溢出错误
int* arr[CAPACITY_DEBUG];是在栈上分配100万个指针,每个指针8字节,总大小约8MB,而Linux默认栈大小只有8MB左右,这直接导致栈溢出,覆盖了栈上的其他数据(比如函数返回地址、局部变量),这就是Valgrind报Invalid write/Invalid read错误的原因。
把栈上的指针数组改成堆分配:
// 替换原来的int* arr[CAPACITY_DEBUG]; int** arr = malloc(CAPACITY_DEBUG * sizeof(int*)); if (!arr) { perror("malloc arr"); exit(1); } // 最后记得释放这个数组 free(arr);
3. free后RES不回落是正常的内存池机制,不是泄漏
glibc的malloc会把free的内存缓存起来,作为内存池供后续malloc复用,不会立刻把内存还给内核,所以top的RES不会立刻下降。这是为了减少系统调用开销、提升性能的优化行为,完全正常,不是内存泄漏。
如果一定要让内存还给内核,可以调用malloc_trim(0);(你代码里注释掉了这个函数),但频繁调用会影响性能,只建议在确实需要释放内存给系统时使用。
4. 正确跟踪内存泄漏的方法
- 先确认是否真的泄漏:Valgrind的
HEAP SUMMARY已经明确显示All heap blocks were freed -- no leaks are possible,说明测试程序没有内存泄漏,实际程序的崩溃可能是其他原因(比如栈溢出、内存越界)。 - 用正确的工具跟踪内存变化:
- 用
/proc/self/status或/proc/self/statm获取当前RES值,而非ru_maxrss。 - 用Valgrind的
--tool=massif生成内存使用快照,分析内存增长的具体来源。 - 用
pmap -x <pid>查看进程的内存分布,确认哪些内存段在增长。
- 用
- 排查实际程序的问题:如果实际程序RES持续增长,先通过Valgrind确认是否有未释放的内存,再检查是否是内存池缓存导致的“假增长”,最后排查是否存在栈溢出、内存越界破坏堆结构的情况。
内容的提问来源于stack exchange,提问作者Nafiul Alam Fuji

