21

次のプログラムでは、ガベージ コレクションが実行されますが、メモリの初期サイズのサイズは回復しません。1. メモリの初期サイズは合計メモリ: 16,940 バイト プライベート バイト 8134656

  1. ループ内にオブジェクトを作成し、これらのオブジェクトにはスコープがないため、ループ外で gc collect が実行されたときにこれらのオブジェクトが解放されるようにしました。

  2. ただし、GC 収集後のメモリは初期サイズと同じではありません 合計メモリ: 29,476 バイト プライベート バイト 8540160 ハンドル数: 115

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MemoryLeakTest
{

    class Program
    {
        static void DisplayMemory()
        {
            Console.WriteLine("Total memory: {0:###,###,###,##0} bytes", GC.GetTotalMemory(true));            
            Console.WriteLine("Private bytes {0}", System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64);
            Console.WriteLine("Handle count: {0}", System.Diagnostics.Process.GetCurrentProcess().HandleCount);
            Console.WriteLine();
        }

        static void Main()
        {
            DisplayMemory();
            GC.Collect();
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("--- New object #{0} ---", i + 1);

                object o = new object();

                GC.Collect();
                DisplayMemory();
            }
            Console.WriteLine("--- press any key to quit ---");            
            Console.WriteLine();
            Console.Read();

            GC.Collect();
            DisplayMemory();
        }

    }
}

Output: 
=======
Total memory: 16,940 bytes
Private bytes 8134656
Handle count: 115

--- New object #1 ---
Total memory: 25,588 bytes
Private bytes 8540160
Handle count: 115

--- New object #2 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- New object #3 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- New object #4 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- New object #5 ---
Total memory: 25,576 bytes
Private bytes 8540160
Handle count: 115

--- press any key to quit ---


Total memory: 29,476 bytes
Private bytes 8540160
Handle count: 115

*********************************

プライベート バイトとマネージド ヒープ サイズが初期サイズから増加した理由は何ですか?

4

5 に答える 5

25

全体的な問題

メモリ使用量をカウントしてレポートする試みは、測定されていた GC オブジェクト割り当てサイズの 10,000 倍のメモリを消費する方法で処理されました。

さらに、ハンドル数の出力は目前の問題には適用できませんでしたが (テストではハンドルが開閉されていなかったため)、大量のメモリ割り当てが発生しました (その数だけを削除すると、割り当ての合計が半分になりました)。

元のプログラムは、60 ~ 120 バイトのオブジェクトの割り当てを測定しようとしていましたが (32 ビット プログラムか 64 ビット プログラムかによって異なります)、呼び出されるたびに 600 KB のメモリが割り当てられる関数を使用して測定しましたそのうち、ラージ オブジェクト ヒープ (LOH) にありました。

これをテストする別の方法が提供されています。これは、GC.Collect 呼び出しの後にすべてのオブジェクトが実際になくなったことを示しています。DisplayMemory 関数のメモリ使用量の詳細も提供されます。

結論

100k オブジェクトを作成して収集しても、マネージ メモリのサイズは増加しません。5 つのオブジェクトのみが作成および収集されると、プロセスのプライベート バイトは約 12 KB 増加しますが、SoS は、それがマネージド ヒープからのものではないことを示しています。非常に小さいサイズとオブジェクト数を扱っている場合、何が起こっているのかを正確に判断することはできません。代わりに、非常に多数のオブジェクトでテストして、何かがリークしているかどうかを非常に簡単に確認できるようにすることをお勧めします。この場合、漏れはなく、何も問題はなく、すべて問題ありません。

分析ツールとアプローチ

このプログラムによるメモリ使用量を確認するために、次の 2 つのツールを使用しました。

  1. VS 2013 Pro - パフォーマンスおよび診断ツール - 最初にこれを実行したところ、元のプログラムが 3.6 MB のメモリを割り当てていることがわかりました。これは、オブジェクトの割り当てから予想される 60 ~ 120 バイトだけではありません。文字列とコンソールへの書き込みでメモリがいくらか使用されることはわかっていましたが、3.6 MB はショックでした。
  2. Son of Strike (SoS) - これは、Visual Studio および WinDbg で動作するデバッガ拡張機能であり、.Net Framework に同梱されています (マシンの各フレームワーク バージョン ディレクトリにある sos.dll を参照してください)。

VS 2013 Pro - パフォーマンスおよび診断ツール - メモ

「プロファイリング方法」を「.NET メモリ割り当て」に設定して、VS 2013 Pro のパフォーマンスおよび診断ツールで元のプログラムを実行した結果を以下に示します。これにより、考えられていたよりも多くのメモリが割り当てられているという非常に簡単な手がかりが得られました。グラフの上にある 3.6 MB の合計割り当てを参照してください。DisplayMemory 呼び出しを削除すると、2,476 バイトに減少します。

パフォーマンスおよび診断ツール - スクリーンショット

ストライクの息子 - メモ

マシンに .Net 4.5 がインストールされていない限り、VS2010 で SoS を使用できます。または、Update3 を使用して VS2012 で使用できます。プロジェクトでアンマネージド デバッグを有効にし、32 ビット プロセスを開始していることを確認してから、VS デバッガーのイミディエイト ウィンドウで ".load sos" を実行してください。この問題を確認するために使用したコマンドは、「!eeheap -gc」と「!dumpheap -stat」です。

代替試験プログラム

class Program
{
    static void Main()
    {
        // A few objects get released by the initial GC.Collect call - the count drops from 108 to 94 objects in one test
        GC.Collect();

        // Set a breakpoint here, run these two sos commands:
        // !eeheap -gc
        // !dumpheap -stat
        for (int i = 0; i < 100000; i++)
        {
            object o = new object();
        }

        // Set a breakpoint here, run these two sos commands before this line, then step over and run them again
        // !eeheap -gc
        // !dumpheap -stat
        GC.Collect();
    }
}

代替テスト結果

概要

100,000 個の System.Object を割り当てて収集すると、最初より 4 個少ないオブジェクトになり、マネージド ヒープ サイズは最初より 900 バイト小さくなります。

ガベージ コレクションは期待どおりに機能しています。

ベースライン - 最初の GC.Collect の後

!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x024f23d0
generation 1 starts at 0x024f100c
generation 2 starts at 0x024f1000
ephemeral segment allocation context: none
         segment             begin         allocated  size
024f0000  024f1000  024f23dc  0x13dc(5084)
Large object heap starts at 0x034f1000
         segment             begin         allocated  size
034f0000  034f1000  034f5380  0x4380(17280)
Total Size:              Size: 0x575c (22364) bytes.
------------------------------
GC Heap Size:    Size: 0x575c (22364) bytes.

!dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
[...]
6ed026b8        1          112 System.AppDomain
6ed025b0        2          168 System.Threading.ThreadAbortException
6ed05d3c        1          284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed03a6c        2          380 System.Int32[]
6ed0349c       20          560 System.RuntimeType
0047fab8       14         1024      Free
6ed02248       32         1692 System.String
6ecefe88        6        17340 System.Object[]
Total 95 objects

100,000 個の System.Object を割り当てた後、最終的な GC.Collect の前

!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x024f23d0
generation 1 starts at 0x024f100c
generation 2 starts at 0x024f1000
ephemeral segment allocation context: none
         segment             begin         allocated  size
024f0000  024f1000  02617ff4  0x126ff4(1208308)
Large object heap starts at 0x034f1000
         segment             begin         allocated  size
034f0000  034f1000  034f5380  0x4380(17280)
Total Size:              Size: 0x12b374 (1225588) bytes.
------------------------------
GC Heap Size:    Size: 0x12b374 (1225588) bytes.

!dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
[...]
6ed024e4        1           84 System.OutOfMemoryException
6ed02390        1           84 System.Exception
6ed026b8        1          112 System.AppDomain
6ed025b0        2          168 System.Threading.ThreadAbortException
6ed05d3c        1          284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed03a6c        2          380 System.Int32[]
6ed0349c       20          560 System.RuntimeType
0047fab8       14         1024      Free
6ed02248       32         1692 System.String
6ecefe88        6        17340 System.Object[]
6ed025e8   100002      1200024 System.Object
Total 100095 objects

最終GC.Collect後

!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x024f2048
generation 1 starts at 0x024f2030
generation 2 starts at 0x024f1000
ephemeral segment allocation context: none
         segment             begin         allocated  size
024f0000  024f1000  024f2054  0x1054(4180)
Large object heap starts at 0x034f1000
         segment             begin         allocated  size
034f0000  034f1000  034f5380  0x4380(17280)
Total Size:              Size: 0x53d4 (21460) bytes.
------------------------------
GC Heap Size:    Size: 0x53d4 (21460) bytes.

!dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
[...]
6ed024e4        1           84 System.OutOfMemoryException
6ed02390        1           84 System.Exception
6ed026b8        1          112 System.AppDomain
0047fab8        9          118      Free
6ed025b0        2          168 System.Threading.ThreadAbortException
6ed05d3c        1          284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed03a6c        2          380 System.Int32[]
6ed0349c       20          560 System.RuntimeType
6ed02248       32         1692 System.String
6ecefe88        6        17340 System.Object[]
Total 91 objects

DisplayMemory 関数のメモリ使用量のレビュー

System.Object の割り当てと比較すると、DisplayMemory はメモリを大量に消費します。文字列 (ヒープ上に置かれる) を作成しており、メモリを取得するために呼び出す関数は、それ自体で大量 (約 600 KB) のメモリを使用しています。

DisplayMemory を呼び出す前のメモリ使用量

!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x02321018
generation 1 starts at 0x0232100c
generation 2 starts at 0x02321000
ephemeral segment allocation context: none
         segment             begin         allocated  size
02320000  02321000  02323ff4  0x2ff4(12276)
Large object heap starts at 0x03321000
         segment             begin         allocated  size
03320000  03321000  03325380  0x4380(17280)
Total Size:              Size: 0x7374 (29556) bytes.
------------------------------
GC Heap Size:    Size: 0x7374 (29556) bytes.

!dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
[...]
6ed05d3c        3          468 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
6ed0349c       20          560 System.RuntimeType
6ed02248       38         2422 System.String
6ecefe88        6        17340 System.Object[]
Total 102 objects

DisplayMemory を呼び出した後のメモリ使用量

!eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x023224fc
generation 1 starts at 0x023224f0
generation 2 starts at 0x02321000
ephemeral segment allocation context: none
         segment             begin         allocated  size
02320000  02321000  02371ff4  0x50ff4(331764)
Large object heap starts at 0x03321000
         segment             begin         allocated  size
03320000  03321000  033653c0  0x443c0(279488)
Total Size:              Size: 0x953b4 (611252) bytes.
------------------------------
GC Heap Size:    Size: 0x953b4 (611252) bytes.

!dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
[...]
6ed02c08        9          954 System.Char[]
006dfac0       17         1090      Free
6ed03aa4      156         1872 System.Int32
6ecffc20      152         3648 System.Collections.ArrayList
6ed05ed4        9         7776 System.Collections.Hashtable+bucket[]
7066e388      152        16416 System.Diagnostics.ProcessInfo
6ed02248      669        20748 System.String
706723e4      152        29184 System.Diagnostics.NtProcessInfoHelper+SystemProcessInformation
6ecefe88      463        48472 System.Object[]
706743a4     2104        75744 System.Diagnostics.ThreadInfo
70666568     2104       151488 System.Diagnostics.NtProcessInfoHelper+SystemThreadInformation
6ed0d640        2       262168 System.Int64[]
Total 6132 objects
于 2013-10-06T02:22:05.367 に答える
3

次の結果があります。

Total memory: 94.804 bytes
Private bytes 19230720
Handle count: 252

--- New object #1 ---
Total memory: 96.932 bytes
Private bytes 19820544
Handle count: 252

--- New object #2 ---
Total memory: 96.932 bytes
Private bytes 19820544
Handle count: 252

--- New object #3 ---
Total memory: 96.932 bytes
Private bytes 19820544
Handle count: 252

--- New object #4 ---
Total memory: 96.932 bytes
Private bytes 19820544
Handle count: 252

--- New object #5 ---
Total memory: 96.932 bytes
Private bytes 19820544
Handle count: 252

--- press any key to quit ---
Total memory: 96.920 bytes
Private bytes 19820544
Handle count: 252

class Program
{
static void DisplayMemory()
{
    Console.WriteLine("Total memory: {0:###,###,###,##0} bytes", GC.GetTotalMemory(true));
    Console.WriteLine("Private bytes {0}", System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64);
  Console.WriteLine("Handle count: {0}", System.Diagnostics.Process.GetCurrentProcess().HandleCount);
    Console.WriteLine();
 }

 static void Main()
 {
  DisplayMemory();
  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();

   for (int i = 0; i < 5; i++)
   {
     Console.WriteLine("--- New object #{0} ---", i + 1);

     object o = new object();

     GC.Collect();
     GC.WaitForPendingFinalizers();
     GC.Collect();

     DisplayMemory();
   }

   Console.WriteLine("--- press any key to quit ---");
  //Console.ReadLine();

  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();

  GC.WaitForFullGCComplete();
  DisplayMemory();

  Console.ReadLine();
}

を削除しConsole.ReadLine();て追加しGC.WaitForPendingFinalizers();ました。あなたが望むように動作するコードは Console.ReadLine() のように見えますが、リソースを直接解放しません!

于 2013-09-11T12:37:46.437 に答える
2

私はあなたのコードをテストしました。得られた最初の 2 つの出力は次のとおりです。

Output: 
=======
Total memory: 16,940 bytes
Private bytes 8134656
Handle count: 115

--- New object #1 ---
Total memory: 25,588 bytes
Private bytes 8540160
Handle count: 115

ここでメモリ使用量が大幅に増加します。これは、DisplayMemory() メソッドで静的クラス Console (Console.WriteLine) を使用しているためです。ループ内でオブジェクトを複数回作成しているからではありません。ループ内でオブジェクトを作成すると、合計メモリが 120 バイトしか作成されませんが、Console クラスを使用すると、合計メモリが約 8000 バイト使用されます。次のコードを試してみてください。理解できます。

static void Main()
        {
            DisplayMemory();
            List<object> objList = new List<object>();
            for (int i = 0; i < 15; i++)
            {
                Console.WriteLine("--- New object #{0} ---", i + 1);

                object o = new object();
                            objList.Add(o);
                DisplayMemory();
            }

            GC.Collect();


            DisplayMemory();
            Console.WriteLine("--- press any key to quit ---");

            Console.ReadLine();
        }

オブジェクトが作成されるたびに、合計メモリが 120 バイト増加することに注意してください。

于 2013-10-09T20:36:07.580 に答える
2

GC.Collect は非同期であるため、GC.Collect(); の直後に DisplayMemory() を呼び出すロジックは次のとおりです。疑問です。 Run GC.Collect synchronouslyには、GC.WaitForPendingFinalizers() に関する詳細が含まれています。

編集 - あなたの質問に答えて:

Total memory: 84,280 bytes
Private bytes 15384576
Handle count: 245

--- New object #1 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #2 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #3 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #4 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

--- New object #5 ---
Total memory: 86,408 bytes
Private bytes 15908864
Handle count: 245

だから、ここでは大きな違いはないようです!

于 2013-09-11T11:40:57.367 に答える
2

合計メモリ: 160,144 バイト プライベート バイト 27189248 ハンドル数: 247

--- 新しいオブジェクト #1 --- s 合計メモリ: 160,152 バイト プライベート バイト 27979776 ハンドル数: 247

--- 新しいオブジェクト #2 --- s 合計メモリ: 160,152 バイト プライベート バイト 27979776 ハンドル数: 247

--- 新しいオブジェクト #3 --- 合計メモリ: 160,152 バイト プライベート バイト 27979776 ハンドル数: 247

--- 新しいオブジェクト #4 --- s 合計メモリ: 160,152 バイト プライベート バイト 27979776 ハンドル数: 247

--- 新しいオブジェクト #5 --- s 合計メモリ: 160,152 バイト プライベート バイト 27975680 ハンドル数: 247

--- 終了するには何かキーを押してください ---

合計メモリ: 160,152 バイト プライベート バイト 27996160 ハンドル数: 247

    static void Main()
    {
        DisplayMemory();
        GC.Collect();
        GC.WaitForFullGCComplete();
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("--- New object #{0} ---", i + 1);

            object o = new object();

            GC.Collect();
            GC.WaitForFullGCComplete();
            Console.Read();
            Console.Read();
            DisplayMemory();

        }
        Console.WriteLine("--- press any key to quit ---");
        Console.WriteLine();
        Console.Read();
        Console.Read();

        GC.Collect();
        GC.WaitForFullGCComplete();
        DisplayMemory();
    }
于 2013-09-11T12:04:56.080 に答える