Lee*_*hai 2 c++ memory-management libstdc++
代码很简单:
#include <vector>
int main() {
std::vector<int> v;
}
Run Code Online (Sandbox Code Playgroud)
然后,我在Linux上使用Valgrind构建并运行它:
g++ test.cc && valgrind ./a.out
==8511== Memcheck, a memory error detector
...
==8511== HEAP SUMMARY:
==8511== in use at exit: 72,704 bytes in 1 blocks
==8511== total heap usage: 1 allocs, 0 frees, 72,704 bytes allocated
==8511==
==8511== LEAK SUMMARY:
==8511== definitely lost: 0 bytes in 0 blocks
==8511== indirectly lost: 0 bytes in 0 blocks
==8511== possibly lost: 0 bytes in 0 blocks
==8511== still reachable: 72,704 bytes in 1 blocks
==8511== suppressed: 0 bytes in 0 blocks
...
==8511== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Run Code Online (Sandbox Code Playgroud)
在这里,即使有1个分配和0个空闲空间,也没有内存泄漏。回答这个问题引述Valgrind的的本段常见问题的解释-
C ++标准库的许多实现都使用它们自己的内存池分配器。大量销毁对象的内存不会立即释放并交还给OS,而是保留在池中供以后重用。
我的主要问题是:
C ++库的实现是如何实现的?它是否在后台处理来自其标准模板的所有分配请求的单独进程,以便在程序退出时(a.out此处),不会立即将内存退还给OS?如果是这样,它将何时退还?我如何检查该过程确实存在?如果没有,那么幕后的“魔力”是什么?
另一个问题:
分配了71 KB。为什么这个号码?
谢谢:)
首先,您不会使用未使用的东西进行测试vector。编译器是聪明的,都gcc和clang在-O2编译上面的代码为空main()(不是单一的其它xor eax, eax设置返回值见装配。在这里同样,对于多数默认构造函数。vector实现(包括gcc和clang)甚至不会分配任何东西-它会等到添加第一个元素后再执行昂贵的分配步骤。
为了获得更具体的结果,请分配一个BIG向量(以便您可以将其与噪声区分开),并将其传递给另一个转换单元(或在单独的.cpp文件中定义)中的方法,如下所示:
#include <vector>
void sink(std::vector<int>& v);
int main() {
std::vector<int> v(12345678);
sink(v);
}
Run Code Online (Sandbox Code Playgroud)
现在,当您检查装配时,您会看到它实际上在做某事。
因此,Valgrind报告的〜72,000字节与您的无关std::vector<int> v,您可能会看到一个完全空白的相同数字。
问题的概念和引用的文档仍然与该问题分开,我将在下面回答。
程序退出时,通常将所有内存释放回操作系统,并且由操作系统强制执行此操作,而不是标准库。操作系统仅清除该进程使用的所有资源,包括未共享的内存分配。当Valgrind提到“退出时正在使用”时,它是在进行操作系统清理之前进行的讨论,因为这是您想知道是否忘记释放任何东西的信息。
您不需要任何单独的过程来处理此问题。它通过Valgrind跟踪malloc和free调用以及其他一些标准分配例程来实现。
您在FAQ中引用的有关使用“使用其自己的内存池分配器”的许多标准库的评论是指这样的想法:标准库可以在调用已知分配调用(例如malloc或operator new 初始调用)的那些之上使用另一个缓存分配层当需要内存时,但是当内存被取消分配时,它将在内部保存在某个列表中,而不是调用相应的取消分配例程(例如free或delete)。
在后续分配中,它将优先使用其内部列表中的内容,而不是返回到标准方法(如果列表已用尽,则必须调用标准例程)。这将使它对于Valgrind不可见,因为Valgrind会认为该应用程序仍在“使用”内存。
由于std::allocator旧版本C ++ 中的内容定义有些无用,因此并未大量使用,而且我不同意“很多”标准库默认情况下使用这种类型的池分配器-至少在今天:我不在其实知道任何做这个了主要标准库的实现之间,但也有一些过去一样。但是,分配器参数是每个容器类的模板参数,因此最终用户也可以执行此自定义,特别是因为allocator在新标准中对接口进行了改进。
在实践中,此类池分配器的最大胜利是(a)使用线程本地的,固定大小的容器分配容器,因为所有包含的对象都具有相同的大小,并且(b)允许分配器在销毁容器时通过一次操作释放所有内容,而不是比逐个元素释放。
您引用的文档有点混乱,因为它谈论的是(不)将内存调整为OS-但它实际上应该说“调整为标准分配例程”。Valgrind不需要将内存返回给OS即可将其视为已释放-它挂接了所有标准例程,并且知道何时在该级别释放了内存。如上所述,标准例程本身会大量“缓存”已分配的内存(这很常见,这与不常见的分配器例程缓存不同),因此,如果Valgrind要求将内存返回给OS,则在报告“退出时分配的内存”将非常无用。 。
| 归档时间: |
|
| 查看次数: |
201 次 |
| 最近记录: |