std::unordered_map 析构函数不释放内存?
Posted
技术标签:
【中文标题】std::unordered_map 析构函数不释放内存?【英文标题】:std::unordered_map destructor does not free memory? 【发布时间】:2020-02-07 11:07:08 【问题描述】:根据标准,当 std::unordered_map 的析构函数被调用时(例如,当它离开创建它的范围时),人们期望它分配的内存被释放。然而,我在多台机器上运行的一个简单实验似乎与此冲突?考虑以下程序:
#include <chrono>
#include <iostream>
#include <map>
#include <unordered_map>
#include <memory>
#include <thread>
#include <vector>
void CreateMap()
std::unordered_map<int, int> testMap;
for (int i=0; i < 10000000; i++)
testMap[i] = 5;
std::cout << "finished building map" << std::endl;
std::this_thread::sleep_for (std::chrono::seconds(15));
std::cout << "about to exit from CreateMap()" << std::endl;
int main()
CreateMap();
CreateMap();
CreateMap();
while (true)
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
在我的机器上,地图构建完成时会消耗 10% 的 RAM,但最后我们在 CreateMap() 中休眠。然而,在退出 RAM 后仅下降到 8%(可以使用各种大小的地图来显示地图本身负责超过 2%)所以人们会期望 CreateMap 泄漏内存?然而,对 CreateMap() 的 3 次调用或仅一次调用似乎没有任何区别(因此内存被回收到程序而不是 RAM?)。
这可能是我不理解的操作系统内存管理的奇怪之处吗?即,程序可以释放内存以供自己将来使用(未来分配)但不能释放给操作系统(即用于内存分配在其他程序中)?
【问题讨论】:
编译器的标准(或运行时)库附带了一个堆管理。每当内存不足时,它都会尝试从操作系统获取更多。当在 C++ 中释放内存时,它不一定会返回给操作系统,但通常可能会保留在“空闲”列表中以供以后的请求使用。因此,在使用操作系统内存检查工具时,您可能看不到这些可用内存。 操作系统不必立即释放为您的进程分配的页面,因此在检查内存时仍然可以计算它们(取决于如何检查内存)。 仅供参考:CS: How is heap memory allocated to a process? 今天早上有人问了一个非常相似的问题。你可能有兴趣阅读this answer。 @Mathemagician:根据操作系统和分配器,它甚至可以通过向操作系统提供提示来协助这个过程,例如在 Linux 上,当没有剩余分配为MADV_DONTNEED
时,它可能会 madvise
mmap
ed 页面(通常是块大小的一小部分),这告诉操作系统它可以删除数据;如果稍后访问这些页面,操作系统只会按需提供它们并填充零。所以从技术上讲,内存仍然是“分配的”(虚拟地址空间仍在使用中,如果需要可以访问内存),但没有使用物理 RAM 或交换文件。
【参考方案1】:
你测试错了。代码不会泄漏,但释放的内存不一定可用于其他进程(您正在测量的内容) - 它可能仍会被同一进程声明以供将来分配。
例如,在移除无限循环并减少i
的限制以适应我的测试沙箱之后,我在 Valgrind 下运行代码:
valgrind --leak-check=full ./60112215
==3396096== Memcheck, a memory error detector
==3396096== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3396096== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==3396096== Command: ./60112215
==3396096==
finished building map
about to exit from CreateMap()
finished building map
about to exit from CreateMap()
finished building map
about to exit from CreateMap()
==3396096==
==3396096== HEAP SUMMARY:
==3396096== in use at exit: 0 bytes in 0 blocks
==3396096== total heap usage: 300,044 allocs, 300,044 frees, 13,053,168 bytes allocated
==3396096==
==3396096== All heap blocks were freed -- no leaks are possible
==3396096==
==3396096== For lists of detected and suppressed errors, rerun with: -s
==3396096== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
如果你想真正亲自演示一下,可以多次调用CreateMap()
,看看进程的内存使用量并没有增加:
int main()
for (auto i = 0; i < 100; ++i)
CreateMap();
这显然是在重复使用上一次迭代中释放的内存。
【讨论】:
【参考方案2】:确保std::unordered_map
不会泄露。您不能使用系统监视器来检查您的程序是否泄漏,期间。如果有的话,您应该查看物理内存使用情况,而不是总内存或虚拟内存。由于页面可以交换到磁盘(页面文件),因此即使物理内存也不能准确反映 RAM 的使用情况。即使那样,例如优化。操作系统或 c++ 语言级别可能会重用堆内存 (as mentioned in comment) 进行优化。涉及的因素太多,而且对于像检测泄漏内存这样微妙的事情来说,它的总价值太大了。寻找泄漏的一种简单(并且通常有效)的方法是在程序终止时查看堆,例如。使用_CrtSetDbgFlag 或类似工具。不用说,避免手动内存管理和使用所有权语义在避免代码泄漏方面大有帮助。
【讨论】:
以上是关于std::unordered_map 析构函数不释放内存?的主要内容,如果未能解决你的问题,请参考以下文章
C++ std::unordered_map 中使用的默认哈希函数是啥?
C++17 复制构造函数,std::unordered_map 上的深拷贝
使用 lambda 函数在 std::unordered_map 中查找最小值