C++删除所有对象后内存未归还给操作系统是什么原因?
测试代码
#include <chrono> #include <cstdio> #include <list> #include <thread> struct a { char data[1024]; }; struct b { char data[1024]; }; struct c { char data[1024]; }; struct d { char data[1024]; }; int main() { std::list<a *> a_list; for (size_t i = 0; i < 1000000; i++) { a_list.push_back(new a); } std::list<b *> b_list; for (size_t i = 0; i < 1000000; i++) { b_list.push_back(new b); } std::list<c *> c_list; for (size_t i = 0; i < 1000000; i++) { c_list.push_back(new c); } std::list<d *> d_list; for (size_t i = 0; i < 1000000; i++) { d_list.push_back(new d); } printf("---\n"); // line A while (a_list.size() > 0) { a *item = a_list.front(); a_list.pop_front(); delete item; } printf("---\n"); while (b_list.size() > 0) { b *item = b_list.front(); b_list.pop_front(); delete item; } printf("---\n"); while (c_list.size() > 0) { c *item = c_list.front(); c_list.pop_front(); delete item; } printf("---\n"); while (d_list.size() > 0) { d *item = d_list.front(); d_list.pop_front(); delete item; } printf("end\n"); // line B std::this_thread::sleep_for(std::chrono::hours(1)); }
现象原因分析
- 用户态内存分配器缓存机制:Linux环境下C/C++程序默认使用glibc的ptmalloc2作为用户态内存分配器,
new/delete底层封装了malloc/free接口。执行delete操作时,内存只是被归还给ptmalloc的内部空闲缓存池,不会立即调用系统调用交还给操作系统,这部分缓存的内存会被后续的new请求直接复用,因此出现第二个观测现象:释放后再分配小于首次总量的内存时,内存使用率不会上升到首次分配后的水平。 - ptmalloc分配方式阈值规则:ptmalloc对内存分配有两种实现方式:小于
MMAP_THRESHOLD阈值(默认128KB,支持动态调整)的小块内存通过brk系统调用从堆区分配,大于等于阈值的大块内存通过mmap匿名映射分配。通过brk分配的内存只有在堆顶连续地址空间全部空闲时才能下调brk指针将内存还给操作系统,只要堆区存在任何未释放的小块内存(包括ptmalloc缓存的碎片内存、std::list节点的分配残留等),都会导致堆顶无法收缩,内存无法交还操作系统。 - 阈值触发的分配方式差异:单个struct的大小为1KB,13万个struct总大小约为130MB,未超过ptmalloc动态调整后的mmap阈值,此时所有内存都通过brk从堆区分配,释放后无法交还操作系统,内存使用率不变;调整到14万个struct时总大小约140MB,超过动态mmap阈值,分配器改用mmap方式分配内存,
delete时会直接调用munmap释放匿名映射区,内存直接交还给操作系统,因此可以观测到内存使用率下降。
内容的提问来源于stack exchange,提问作者Keto
相关产品推荐
相关产品推荐

