C++ std :: vector的行为就像在特定情况下泄漏内存一样

Joh*_*ohn 6 memory-leaks vector c++11

我发现了一种情况,其中向量的行为类似于泄漏内存,并且可以将其归结为最小的工作示例.在这个例子中,我(在一个函数中)生成一个包含三个char向量的向量.首先,这些char矢量被推入大量元素,它们的容量缩小到它们的大小.然后在大矢量上分配单元素大小的矢量.现在的问题是,使用的内存太大,即使函数返回并且向量被破坏,内存也不会被释放.我如何获得回忆?为什么它显示这个bahaviour?我该怎么做才能避免这种泄漏行为?

这里是示例代码(抱歉长度):

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

using namespace std;

// see http://man7.org/linux/man-pages/man5/proc.5.html at /proc/[pid]/status
string meminfo() {
    // memory information is in lines 11 - 20 of /proc/self/status
    ifstream stat_stream("/proc/self/status",ios_base::in);

    // get VmSize from line 12
    string s;
    for ( int linenum = 0; linenum < 12; ++linenum )
        getline(stat_stream,s);
    stat_stream.close();
    return s;
}

void f() {
    vector<vector<char>> mem(3); // with 1,2 memory is fine
    size_t size = 16777215;      // with 16777216 or greater memory is fine
    for ( vector<char>& v : mem ) {
        for ( unsigned int i = 0; i < size; ++i )
            v.push_back(i);
        v.shrink_to_fit();       // without this call memory is fine
    }

    cout << "Allocated vectors with capacities ";
    for ( vector<char>& v : mem )
        cout << v.capacity() << ", ";
    cout << endl << "used memory is now:   " << meminfo() << endl;

    for ( vector<char>& v : mem ) {
        v = vector<char>{1};
        if ( v.size() != v.capacity() )
            cout << "Capacity larger than size." << endl;
    }
    cout << "Shrinked vectors down to capacity 1." << endl
        << "Used memory is now:   " << meminfo() << endl;
}

int main() {
    cout << "At beginning of main: " << meminfo() << endl;
    f();
    cout << "At end of main:       " << meminfo() << endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我机器上的输出:

At beginning of main: VmSize:      12516 kB
Allocated vectors with capacities 16777215, 16777215, 16777215,
used memory is now:   VmSize:      78060 kB
Shrinked vectors down to capacity 1.
Used memory is now:   VmSize:      61672 kB
At end of main:       VmSize:      61672 kB
Run Code Online (Sandbox Code Playgroud)

但是,valgrind看不到内存泄漏.

我想这个例子中的参数是系统相关的,以显示奇怪的行为.我使用Linux Mint Debian Edition和g ++ 4.8.2以及x86_64内核.我编译:

g++ -std=c++11 -O0 -Wall memory.cpp -o memory
Run Code Online (Sandbox Code Playgroud)

并尝试-O3并没有明确的优化设置.

一些有趣的观点是:

  • 当我更换v = vector<char>{1};v.clear(); v.shrink_to_fit(); v.push_back(1);问题保持不变.通过v = vector<char>(16777215);"解决"内存问题来替换大矢量的推动和收缩.
  • 16777215 = 2 ^ 24 - 1,所以也许它必须对内存边界做一些事情.
  • 此外,人们可以期望程序在main(12516 kiB)的开头使用的存储器加上大矢量的存储器,程序应该总共使用大约3*16777216 B + 12516 kiB = 61668 kiB,这大约是它最终使用的内存.

在现实世界的应用程序中,我使用向量来收集应该应用于FEM模拟的刚度矩阵的操作.由于我想达到可用内存的可能性(也就速度而言),我需要保存不同的内存以避免交换.由于交换确实发生,我认为VmSize值是可靠的.

Chr*_*odd 4

问题是您误解了 C++ 上下文中“释放”内存的含义。当您的应用程序释放内存(使用shrink_to_fit或删除对象或其他方式)时,它实际上只是将内存释放给 C++ 运行时,而不一定将其释放回系统以供其他进程使用。C++ 运行时可以选择保留内存以便稍后在同一进程中重用。

通常,当内存碎片化时,就会发生这种情况——空闲内存(在程序的虚拟机空间中)被正在使用的内存包围。只有当释放的内存位于程序内存空间的末尾时,C++运行时才会选择(或能够)将其返回给系统。

一般来说,这种内存保留不是问题,因为当应用程序请求更多内存时,通常可以重用它。您可能遇到的问题是,由于 C++ 运行时无法移动正在使用的内存块,因此您可能无法重用太小的空闲块。运行时可能会使用各种技巧和启发法来尝试避免这种情况,但它们并不总是有效。