.NETにおけるLarge Object Heap(LOH)の使用について
.NETのメモリ領域には大きく2種類存在します。
1つは80,000バイト未満のオブジェクトを格納するSmall Object Heap(SOH)と、
80,000バイト以上のオブジェクトを格納するLarge Object Heap(LOH)です。


この80,000バイトというサイズは将来的に変わる可能性のある数値であり、定数として考えるべきではありません。


ガベージコレクションを行った場合、SOHではコンパクションが発生して、空いている領域を詰めてメモリの中身を再配置します。
LOHはガベージコレクションにより、コンパクションは発生せず、メモリの再配置はしません。
このことは、メモリの断片化を生み、OutOfMemoryExceptionの例外を発生させることを意味します。


Andrew Hunterは、この件に関しての最悪の場合の事例を再現したテストプログラムを公開しています。

The Dangers of the Large Object Heap
https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/


.NETのガベージコレクションはヒープの末尾に領域を確保する傾向があります。そのため、メモリの途中で十分な空き容量があっても、メモリの確保に失敗することがあります。
この記事では、本来なら何百MB以上利用できるのにもかかわらず、20MBしか使用できなくなりました。



実は、この結果は.NET4.0において劇的に改善されました。



以下に同様のプログラムで行った実験結果を示します。

実験環境
VisualStudio2013 Expressでx86のアプリケーションを作成
Windows7 SP1 64bit
メモリ 4.00GB
結果

v2.0 v3.0 v3.5 v4.0 v4.5 v4.5.1
With large bloks 19Mb 19Mb 20Mb 1546Mb 1546Mb 1439Mb
With large bloks,
frequent garbage collection
23Mb 23Mb 23Mb 1570Mb 1570Mb 1546Mb
Only small block 1572Mb 1620Mb 1588Mb 1643Mb 1643Mb 1643Mb
With large blocks,
large blocks not growing
1375Mb 1596Mb 1419Mb 1636Mb 1620Mb 1565Mb


参考:
C++のアンマネージドコードで「With large bloks」を行った場合の結果
 2020MB

結果をみると.NET4.0から劇的にメモリの確保の効率がよくなっていることがわかります。
ただし、最終的に断片化が解決したわけではありません。


なお、.NET4.5.1ではLOHの領域にたいするコンパクションを指示する命令が追加されました。

Using System;
Using System.Runtime;

// LOHに対するコンパクションを要求
GCSettings.LOHCompactionMode =
GCLargeObjectHeapCompactionMode.CompactOnce;

// GCが発生してLOHがコンパクションされる
GC.Collect():
この設定でGCが発生した場合、停止時間が長くなる可能性が極めて高いので使用には注意を払ってください。














C++でのテストコード

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
    int a;
    char* pSmallBlock;
    char* pLargeBlock;
    int blockCount;
    const int blockSize = 90000;
    int largeBlockSize = 1 << 24;
    try
    {
        for (;;)
        {
            if ((blockCount % 1000) == 0)
            {
                printf("%d\n", blockCount);
            }
            pLargeBlock = new char[blockCount];
            pSmallBlock = new char[blockSize];
            ++blockCount;
            ++largeBlockSize;
            delete pLargeBlock;
        }
    }
    catch (...)
    {
        printf("Size %d\n", (blockCount * blockSize) / (1024 * 1024));
    }
    scanf("%d", &a);
    return 0;

}



[PR]
by mima_ita | 2013-12-03 22:02 | .NET
<< .NETのCLRは文字をUTF... Pythonでツイッターの検索を行う >>



実験ですお
検索
カテゴリ
最新の記事
.NET4.5におけるasy..
at 2014-07-02 00:46
.NETでTwitterを検..
at 2014-06-29 00:49
Redmineのプラグインで..
at 2014-06-28 03:29
IO.popenのwrite..
at 2014-06-28 03:25
RedmineのWikiでU..
at 2014-06-28 03:16
以前の記事
最新のトラックバック
その他のジャンル
ブログパーツ