7

私はMSVC9.0を搭載したWindows7でC++を使用しており、MSVC9.0を搭載したWindowsXPSP3でテストおよび再現することもできました。

0.5MBサイズのオブジェクトを1GB割り当てた場合、それらを削除すると、すべて問題なく動作し、期待どおりに動作します。ただし、0.25MBサイズのオブジェクトを削除するときに1GBを割り当てると、メモリは予約されたままになり(アドレス空間モニターでは黄色)、それ以降は0.25MB未満の割り当てにのみ使用できます。

この単純なコードでは、typedefする構造体を変更することで両方のシナリオをテストできます。構造体を割り当てて削除した後、1GBの1MB charバッファーを割り当てて、charバッファーが構造体がかつて占有していたメモリを使用するかどうかを確認します。

struct HalfMegStruct
{
    HalfMegStruct():m_Next(0){}

    /* return the number of objects needed to allocate one gig */
    static int getIterations(){ return 2048; }

    int m_Data[131071];
    HalfMegStruct* m_Next;
};

struct QuarterMegStruct
{
    QuarterMegStruct():m_Next(0){}

    /* return the number of objects needed to allocate one gig */
    static int getIterations(){ return 4096; }

    int m_Data[65535];
    QuarterMegStruct* m_Next;
};

// which struct to use
typedef QuarterMegStruct UseType;

int main()
{
    UseType* first = new UseType;
    UseType* current = first;

    for ( int i = 0; i < UseType::getIterations(); ++i )
        current = current->m_Next = new UseType;

    while ( first->m_Next )
    {
        UseType* temp = first->m_Next;
        delete first;
        first = temp;
    }

    delete first;

    for ( unsigned int i = 0; i < 1024; ++i )
        // one meg buffer, i'm aware this is a leak but its for illustrative purposes. 
        new char[ 1048576 ]; 

    return 0;
}

以下に、アドレス空間モニター内からの私の結果を示します。これら2つの最終結果の唯一の違いは、1GBマーカーまで割り当てられる構造体のサイズであることを強調しておきます。

クォーターメグ ハーフメグ

これは私には非常に深刻な問題のように思えます。多くの人が苦しんでいて、それを知らない可能性もあります。

  • それで、これは設計によるものですか、それともこれはバグと見なされるべきですか?
  • 小さな削除されたオブジェクトを、より大きな割り当てで実際に無料で使用できるようにすることはできますか?
  • そして、好奇心から、MacまたはLinuxマシンも同じ問題に苦しんでいますか?
4

5 に答える 5

9

これが事実であると断言することはできませんが、これはメモリの断片化のように見えます(多くの形式の1つです)。アロケータ(malloc)は、メモリを解放した後、メモリを解放した後、同じサイズの後で同じサイズの割り当てを処理できるようにバケットを保持するのではなく、異なるサイズのバケットを保持して高速割り当てを有効にする場合があります。同じメモリ。この場合、メモリは同じサイズの追加の割り当てに使用できます。

このタイプの最適化は、使用されていない場合でもメモリを予約する必要があるため、通常、大きなオブジェクトでは無効になっています。しきい値が2つのサイズの間のどこかにある場合、それが動作を説明します。

これは奇妙に見えるかもしれませんが、ほとんどのプログラム(テストではありませんが、実際のプログラム)では、メモリ使用パターンが繰り返されます。一度100kブロックを要求した場合、多くの場合、再度実行します。 。また、メモリを予約しておくと、パフォーマンスが向上し、同じバケットから許可されたすべてのリクエストから生じる断片化を実際に減らすことができます。

時間をかけたい場合は、動作を分析することでアロケータがどのように機能するかを学ぶことができます。サイズXを取得して解放し、サイズYを取得してから、メモリ使用量を表示するテストをいくつか作成します。Xの値を修正し、Yで遊んでください。両方のサイズのリクエストが同じバケットから許可されている場合、予約済み/未使用のメモリはありません(左側の画像)。一方、サイズが異なるバケットから許可されている場合は、次のように表示されます。右の画像への影響。

私は通常、Windowsのコードを作成していません。また、Windows 7も持っていないので、これが当てはまると断言することはできませんが、そのように見えます。

于 2011-03-24T08:54:29.527 に答える
2

Windows7のg++4.4.0でも同じ動作を確認できるので、コンパイラーにはありません。実際、getIterations()返品3590以上の場合、プログラムは失敗します-同じカットオフを取得しますか?これは、Windowsシステムのメモリ割り当てのバグのようです。知識のある人がメモリの断片化について話すのは非常に良いことですが、ここではすべてが削除されているため、観察された動作は絶対に起こらないはずです。

于 2011-03-24T09:08:43.837 に答える
1

あなたのコードを使用して、私はあなたのテストを実行し、同じ結果を得ました。この場合、DavidRodríguezが正しいと思います。

私はテストを実行し、あなたと同じ結果を得ました。この「バケット」動作が発生している可能性があります。

私も2つの異なるテストを試しました。1MBのバッファを使用して1GBのデータを割り当てる代わりに、削除後にメモリが最初に割り当てられたのと同じ方法で割り当てました。クリーンアップされたハーフメガバッファを割り当てた2番目のテストでは、クォーターメガバッファを割り当て、それぞれに最大512MBを追加しました。どちらのテストでも、最終的に同じメモリ結果が得られました。512のみに、予約済みメモリの大きなチャンクが割り当てられていません。

Davidが言及しているように、ほとんどのアプリケーションは同じサイズの割り当てを行う傾向があります。しかし、なぜこれが問題になるのかがはっきりとわかります。

おそらくこれに対する解決策は、この方法で多くの小さなオブジェクトを割り当てる場合は、メモリの大きなブロックを割り当てて自分で管理する方がよいということです。次に、完了したら、大きなブロックを解放します。

于 2011-03-24T10:22:34.733 に答える
1

私はこの件についていくつかの当局と話をしました(グレッグ、あなたがそこにいるなら、こんにちは; Dと言ってください)そしてデビッドが言っていることが基本的に正しいことを確認することができます。

〜0.25MBのオブジェクトを割り当てる最初のパスでヒープが大きくなると、ヒープはメモリを予約してコミットします。削除パスでヒープが縮小すると、ヒープはある程度のペースでデコミットしますが、割り当てパスで予約した仮想アドレス範囲を必ずしも解放するわけではありません。最後の割り当てパスでは、1MBの割り当てはサイズが原因でヒープをバイパスしているため、VAのヒープとの競合が開始されます。

ヒープはVAを予約しており、コミットされたままではないことに 注意してください。興味がある場合は、VirtualAllocVirtualFreeが違いを説明するのに役立ちます。この事実は、プロセスが仮想アドレス空間を使い果たしたという問題に遭遇したことを解決しません。

于 2011-03-25T20:50:06.477 に答える
0

これは、低フラグメンテーションヒープの副作用です。

http://msdn.microsoft.com/en-us/library/aa366750(v=vs.85).aspx

それが役立つかどうかを確認するには、無効にしてみてください。GetProcessHeapとCRTヒープ(および作成した他のヒープ)の両方に対して実行します。

于 2011-03-25T02:26:39.103 に答える