【问题标题】:Free memory from a long vector从长向量中释放内存
【发布时间】:2011-06-29 20:40:33
【问题描述】:

我想做的是释放vector(比如vector<vector<int>>)使用的内存,我使用了交换技巧,即v.swap(vector<vector<int>>())

但是,我观察到交换技巧对短向量很有效,但不适用于长向量,例如我尝试了一个长度为 10,000,000 的向量,它占用了 1.4G 内存,交换后仍有 1.0G 未释放.

下面是我用来测试的代码。

提前感谢您的任何想法!

#include <iostream>
#include <vector>

using namespace std;

typedef unsigned long long     int64;

int main()
{
    {
        vector<vector<int64>> batch;
        {
            vector<int64> v;
            for (size_t i = 0; i < 12; ++i)
                v.push_back(8000000000);
            for (size_t i = 0; i < 10000000; ++i)
                batch.push_back(v);
        }
        cout << "pause 1" << endl;
        cin.ignore();
        {
            vector<vector<int64>> tmp;
            batch.swap(tmp);
            // I tried several things here.
            //tmp.swap(batch);
            //batch.clear();
            //batch.shrink_to_fit();
            //batch = tmp;
        }
        cout << "pause 2" << endl;
        cin.ignore();
    }
    cout << "pause 3" << endl;
    cin.ignore();
}

【问题讨论】:

  • 您如何测量“内存使用情况”?在程序中释放内存并不一定意味着运行时会将内存返回给操作系统(或者操作系统会回收它)。
  • 操作系统可能会缓存您的内存分配。内存分配很昂贵,因此操作系统可能会“认为”由于您的程序将重复分配大量内存,因此不会将它们标记为空闲。
  • 哪个操作系统?哪个编译器?操作系统(以及编译器、运行时......)通常对大内存分配有不同的内存分配规则。
  • 我在Ubuntu 10.04中使用G++4.6,我使用的内存使用度量是“top”中显示的数字。
  • 如果是因为操作系统做了缓存,是否可以禁用缓存?

标签: c++ stl vector memory-management


【解决方案1】:

James、In silico 和 Max 的言论都是正确的。您希望在调用batch.swap(tmp) 时释放内存,因为tmp 是空的。我刚刚在使用 gcc 4.6.0 编译的 64 位 Linux 上运行了您的程序。使用 top 观察内存消耗时,它变为 2 GB -> 1.8 GB -> 1.8 GB。但是,当添加batch2 并在pause 2 之后填充它,就像填充batch 一样,内存消耗不会增加。运行 batch.swap(tmp) 被注释掉的版本,因此实际上有两个巨大的向量,内存消耗为 2 GB -> 3.2 GB -> 2.8 GB -> 2.8 GB。

这是添加了命令行选项的完整代码:

#include <iostream>
#include <vector>
#include <string.h>

using namespace std;

typedef unsigned long long     int64;

int main(int argc, char** argv)
{
    bool no_swap = (argc > 1 && strcmp(argv[1], "-noswap") == 0); 
    {
        vector<vector<int64> > batch;
        vector<vector<int64> > batch2;
        {
            vector<int64> v;
            for (size_t i = 0; i < 12; ++i)
                v.push_back(8000000000);
            for (size_t i = 0; i < 10000000; ++i)
                batch.push_back(v);
        }
        cout << "pause 1" << endl;
        cin.ignore();
        {
            vector<vector<int64> > tmp;
            if (no_swap) {
                cout << "NOT calling batch.swap(tmp)" << endl;
            } else {                    
                cout << "calling batch.swap(tmp)" << endl;
                batch.swap(tmp);
            }
        }
        cout << "pause 2" << endl;
        cin.ignore();
        {
            vector<int64> v2;
            for (size_t i = 0; i < 12; ++i)
                v2.push_back(8000000000);
            for (size_t i = 0; i < 10000000; ++i)
                batch2.push_back(v2);
        }
        cout << "pause 3" << endl;
        cin.ignore();
    }
    cout << "pause 4" << endl;
    cin.ignore();
}

可以在herehere 中找到对此行为的一些解释。来自后者:

在使用中,分配器可以使用实现指定的策略来分配和释放 和启发式。因此,每次调用分配器对象的分配成员 函数实际上可能不会调用全局运算符 new。这种情况也是 重复调用 deallocate 成员函数。

根据这个文档,可以通过设置GLIBCXX_FORCE_NEW来禁用缓存,但这对我不起作用...

【讨论】:

  • 是否可以禁用缓存?我想做的是释放一个进程使用的内存,以便可以启动另一个使用大内存的进程。如果我们可以允许其他进程使用未释放的内存,那也很好。
  • @Larry:当现在有另一个进程需要内存时,操作系统将足够智能,以便在不使用时将内存提供给该进程。即使它们被缓存算法“保留”。
  • @In silico 操作系统不知道第一个进程不再需要内存。它所能做的就是换掉未使用的内存。通常这工作得很好,但我可以看到在这种情况下可以避免涉及的磁盘 I/O。
  • @ahans:这取决于 C++ 运行时的实现方式。 C++ 运行时可能足够聪明,可以告诉操作系统某些内存块实际上没有被使用。我知道在 Windows 7 下,操作系统区分了“待机”和“空闲”内存,这两者都可以用来满足任何进程的内存需求。
  • @In silico 我不了解 Windows,但我很确定 Linux 中的用户空间应用程序使用 brk 系统调用分配内存。据我所知,没有办法告诉操作系统以后可能会再次使用某个内存区域,您所能做的就是更改数据段大小,这实际上意味着分配或取消分配内存。
【解决方案2】:

很可能您的 CRT 实现认为最好不要将内存还给操作系统,以防您需要另外 12 个 80MB 的连续分配。

【讨论】:

    猜你喜欢
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 2016-11-17
    • 2014-06-24
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多