1

ノードのミリオインの情報を含む大量のストリーミングデータを処理するC++で書いています。ベクトルを使用して、各ノードの名前とインデックス用のマップを保存します。

ここで問題となるのは、ベクトルが予想よりもはるかに多くのメモリを使用しており、それらの破壊が説明できないことです。

somefileに100万行が含まれ、各行が50文字を超えるとします。それらを2回読み込んでから、プロセスのメモリ使用量とベクトルによる推定メモリ使用量を確認します。それらは60MBで異なります。これは私が抱えているより大きな問題のほんの小さな予測であり、GBスケールが異なる場合があります。

Windows7 SP1 Ultimate64ビットでVS2010SP1を使用して、x86設定でプログラムをコンパイルします。

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <Windows.h>
#include <Psapi.h>
using namespace std;

//#define COUNT 500000
int COUNT = 0;

vector<string> namesVector;
map<string,int> namesMap;
void ProcessStatistics()
{   
    PROCESS_MEMORY_COUNTERS memCounter;
    GetProcessMemoryInfo(GetCurrentProcess(),&memCounter,sizeof(memCounter));
    cout<<"Mem Usage by Process: "<<memCounter.WorkingSetSize * 1.0e-6f<<" MB."<<endl;
}
void VectorMemUsage()
{   
    COUNT = namesVector.size();
    int overhead = StringOverhead();
    double mem = 0;
    mem += sizeof(vector<string>);
    mem += overhead*COUNT;
    for(int i=0; i<COUNT; i++)
    {
        mem += namesVector[i].capacity();
    }
    cout<<"Calculated String Vector Usage: "<<mem * 1.0e-6f<<" MB of "<<COUNT<<" strings."<<endl;
}
int StringOverhead()
{
    int overhead = sizeof(string);
    cout<<"String overhead: "<<overhead<<" Bytes."<<endl;   
    return overhead;
}

void main(){
    const std::string infile = "somefile";
    ifstream infstream(infile);
    string s;   
    while(getline(infstream,s) != NULL)
    {
        namesVector.push_back(s);
        //namesMap.insert(pair<string,int>(s,namesVector.size()));
    }
    infstream.clear();
    infstream.seekg(0,ios::beg);    
    while(getline(infstream,s) != NULL)
    {
        namesVector.push_back(s);
        //namesMap.insert(pair<string,int>(s,namesVector.size()));
    }
    //Check process and vector memory usage:
    ProcessStatistics();
    VectorMemUsage();
    System("pause");

    //Release the vector.
    cout<<"Now releasing the memory..."<<endl;        
    //vector<string>(namesVector).swap(namesVector);
    //vector<string>().swap(namesVector); //Deallocate Vector
//map<string,int>().swap(namesMap);   //Deallocate Map
    cout<<"Capacity of vector "<<namesVector.capacity()<<endl;
    ProcessStatistics();
 }

プログラム出力のx86バージョンは次のようになります。

Mem Usage by Process: 336.523 MB.
String overhead: 28 Bytes.
Calculated String Vector Usage: 301.599 MB of 3385108 strings.
Press any key to continue . . .
Now releasing the memory...
Mem Usage by Process: 7.64314 MB.

vectorでnamesVector.shrink_to_fit()またはvector(namesVector).swap(namesVector)イディオムを呼び出すと、ベクトル容量は実際に減少しますが、メモリ使用量が高くなりました。これを修正するアイデアはありますか?スワップトリックはポインタスワップであると想定されていますか?なぜそれはメモリコピーとすべてを含み、これを引き起こすのでしょうか?

Mem Usage by Process: 336.536 MB.
String overhead: 28 Bytes.
Calculated String Usage: 301.599 MB of 3385108 strings.
Vector Capacity is 3543306.
Calculated String Vector Usage: 315.693 MB of 3385108 strings.
Now releasing the memory...
Capacity of vector 3385108
Mem Usage by Process: 434.5 MB.

文字列インデックスのマップを追加すると、予期しない動作が発生しました。vector()。swap(namesVector)とmap()。swap(namesMap)の両方を呼び出すと、結果は次のようになります。これは、メモリが解放されるため、かなり問題ありません。

Mem Usage by Process: 534.778 MB.
String overhead: 28 Bytes.
Calculated String Usage: 301.599 MB of 3385108 strings.
Vector Capacity is 3543306.
Calculated String Vector Usage: 315.693 MB of 3385108 strings.
Press any key to continue . . .
Now releasing the memory...
Capacity of vector 0
Mem Usage by Process: 8.2903 MB.

しかし、vector()。swap(namesVector)のみを呼び出すと、メモリが部分的に解放されます。部分的には、上記の結果(約336 MB)よりもリリースが少ないことを意味します。

Mem Usage by Process: **534.77** MB.
String overhead: 28 Bytes.
Calculated String Usage: 301.599 MB of 3385108 strings.
Vector Capacity is 3543306.
Calculated String Vector Usage: 315.693 MB of 3385108 strings.
Press any key to continue . . .
Now releasing the memory...
Capacity of vector 0
Mem Usage by Process: **440.459** MB.

またはmap()。swap(namesMap)の場合、メモリはほとんど解放されません。

Mem Usage by Process: **534.774** MB.
String overhead: 28 Bytes.
Calculated String Usage: 301.599 MB of 3385108 strings.
Vector Capacity is 3543306.
Calculated String Vector Usage: 315.693 MB of 3385108 strings.
Press any key to continue . . .
Now releasing the memory...
Capacity of vector 3543306
Mem Usage by Process: **535.441** MB.

何が起こったのか説明できません。ここで何が起こったのか誰にも分かりますか?

助けてくれてありがとう。

一番。

4

1 に答える 1

2

メモリ リーク タグはここでは適切ではありません。メモリ リークはまったくありません。すべてのメモリは到達可能であり、まだスコープ内にあるオブジェクトによって所有されています。メモリ リークは、メモリへの参照がないために決して解放できない失われたメモリを意味します。

VectorMemUsage使用するか、overhead*namesVector.capacity()割り当てられたが初期化されていないメモリではなく、設定されたベクトル要素のみをカウントする必要があります。とにかくその関数がグローバル変数を使用するのはなぜですか? 次のように書くとよいでしょう。

void VectorMemUsage()
{   
    int overhead = StringOverhead();
    double mem = 0;
    mem += sizeof(vector<string>);
    mem += overhead*namesVector.capacity();
    for(int i=0; i < namesVector.size(); i++)
    {
        mem += namesVector[i].capacity();
    }
    cout<<"Calculated String Vector Usage: "<<mem * 1.0e-6f<<" MB of " << namesVector.size() <<" strings."<<endl;
}

ベクター内の未使用の容量を回避したい場合は、ベクターに含まれる要素の数 (つまり、入力ファイルに含まれる行数) を事前に把握しreserve、適切な数の要素を事前に割り当てるために使用する必要があります。

ベクトルでshrink_to_fitまたはswapイディオムを呼び出すと、ベクトル容量は実際に減少しますが、メモリ使用量が高くなりました。これを修正するアイデアはありますか? スワップ トリックはポインタ スワップであるはずですか?

いいえ、それだけでは割り当てられたメモリは減りません! 要素を新しいベクター (必要なだけ大きい) にコピーしてからポインターのスワップを行います。したがって、すべての要素のコピーが一時的に 2 つあるため、ピーク メモリが高くなります。

または map().swap() 、メモリはまったく解放されないに近いです。

ベクトルは、スワップ トリック (またはshrink_to_fit()) を使用しない限りメモリを解放しません。現在の容量を保持し、クリアした場合にのみサイズを縮小します。未使用の容量を解放するには、スワップ トリックまたはshrink_to_fit(). したがって、すべてのメモリは引き続きベクトルによって所有されます。

にスワップ トリックを使用する意味はありません。マップは、使用されていない割り当てられたメモリを決して保持しないため、マップで使用されているすべてのメモリを解放するstd::mapことができます。namesMap.clear()

要約すると、コンテナの仕組みから、これは完全に予期されたものです。リークはありません。無効な仮定があるだけです。

于 2012-12-30T14:31:05.163 に答える