大量小字符串分配后内存未释放异常问题排查
问题:字符串处理工具内存占用异常排查
我正在开发一款需处理大量字符串的工具,测试时通过top工具发现程序内存占用持续增长,经长时间分析及valgrind等工具排查,排除了自身代码内存泄漏的可能。起初认为是内存碎片导致,但进一步测试后发现无法解释的现象:
测试代码
#include <string> #include <iostream> #include <unistd.h> #include <array> #define SIZE_LITTLE 100 #define SIZE_BIG 500000 struct Test { std::string data; }; int main(){ { { std::string line = "very long string, i tested using a 15Mb string"; std::array<struct Test*, SIZE_LITTLE> arr; for (int i = 0; i < SIZE_LITTLE; i++){ arr[i] = new Test{line}; } std::cout << "Allocated" << std::endl; sleep(5); for (int i = 0; i < SIZE_LITTLE; i++){ delete arr[i]; } std::cout << "Memory released" << std::endl; sleep(5); } std::cout << "Memory released out of scope" << std::endl; sleep(5); } { { std::string line = "smaller string"; std::array<struct Test*, SIZE_BIG> arr; for (int i = 0; i < SIZE_BIG; i++){ arr[i] = new Test{line}; } std::cout << "Allocated" << std::endl; sleep(5); for (int i = 0; i < SIZE_BIG; i++){ delete arr[i]; } std::cout << "Memory released" << std::endl; sleep(5); } std::cout << "Memory released out of scope" << std::endl; sleep(5); } }
运行代码后观察到:第一个代码块符合预期,分配后内存占用高,删除指针后内存回到接近0;第二个代码块分配后内存占用高,但删除所有指针及变量出作用域后内存始终不下降。连续运行第二个代码块,预期内存占用会更高,但实际全程保持不变。
原因分析
这是内存分配器的缓存策略导致的,和内存碎片无关,核心细节如下:
大内存与小内存的分配/释放逻辑差异
- 第一个代码块中的15MB字符串属于大内存分配,主流内存分配器(如glibc的ptmalloc)会直接通过
mmap向操作系统申请内存。当释放这类大内存时,分配器会调用munmap直接将内存还给操作系统,所以top工具能看到内存占用明显下降。 - 第二个代码块中的"smaller string"属于短字符串,结合
std::string的小字符串优化(SSO),Test对象整体是小内存块。分配器对小内存块的处理逻辑不同:释放后不会立刻还给操作系统,而是将其缓存到内部的空闲内存池中,留待后续同规格的内存申请直接复用,避免频繁向系统申请/释放内存的性能开销。
- 第一个代码块中的15MB字符串属于大内存分配,主流内存分配器(如glibc的ptmalloc)会直接通过
重复运行内存不变的原因
第二次运行第二个代码块时,分配器直接从缓存的空闲内存池中取出内存块分配,不需要再向操作系统申请新内存,因此top显示的内存占用不会上升。top指标的局限性top的RES(常驻内存)指标统计的是程序当前占用的物理内存,包括分配器缓存的空闲内存。这些缓存的内存虽然没被程序主动使用,但分配器会保留它们,直到系统内存紧张时才会主动归还给操作系统。如果要查看分配器内部的内存使用细节,可以调用malloc_stats()或malloc_info()函数输出更精准的分配数据。
内容的提问来源于stack exchange,提问作者Zoraphie
相关产品推荐
相关产品推荐

