web-dev-qa-db-ja.com

.NETアプリケーションのメモリ使用量を削減しますか?

.NETアプリケーションのメモリ使用量を削減するためのヒントは何ですか?次の簡単なC#プログラムを検討してください。

class Program
{
    static void Main(string[] args)
    {
        Console.ReadLine();
    }
}

x64releaseモードでコンパイルされ、Visual Studioの外部で実行されているタスクマネージャーは次を報告します。

Working Set:          9364k
Private Working Set:  2500k
Commit Size:         17480k

x86のためだけにコンパイルされている場合は、少し改善されます。

Working Set:          5888k
Private Working Set:  1280k
Commit Size:          7012k

次に、次のプログラムを試してみました。これは同じことですが、実行時の初期化後にプロセスサイズを調整しようとします。

class Program
{
    static void Main(string[] args)
    {
        minimizeMemory();
        Console.ReadLine();
    }

    private static void minimizeMemory()
    {
        GC.Collect(GC.MaxGeneration);
        GC.WaitForPendingFinalizers();
        SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
            (UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
    }

    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetProcessWorkingSetSize(IntPtr process,
        UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
}

Visual Studioの外部でのx86Releaseの結果:

Working Set:          2300k
Private Working Set:   964k
Commit Size:          8408k

これは少し優れていますが、そのような単純なプログラムでは依然として過剰に思えます。 C#プロセスを少しスリムにするトリックはありますか?ほとんどの場合、バックグラウンドで実行するように設計されたプログラムを作成しています。私はすでに別の アプリケーションドメイン でユーザーインターフェイスの処理を行っていますが、これはユーザーインターフェイスの処理を安全にアンロードできることを意味しますが、バックグラウンドに座っているだけで10MBを占有しているように見えます。

P.S。なぜ私が気にするのか---(パワー)ユーザーはこれらのことを心配する傾向があります。パフォーマンスにほとんど影響を与えない場合でも、技術に精通したユーザー(私の対象読者)は、バックグラウンドアプリケーションのメモリ使用量についてヒッピーな傾向にありがちです。 Adobe Updaterが11MBのメモリを消費しているのを見て、Foobar2000の落ち着いたタッチに癒されて気分が悪くなりました。現代のオペレーティングシステムでは、このようなことは技術的にそれほど重要ではありませんが、認識に影響がないわけではありません。

106
Robert Fraser
  1. スタックオーバーフローの質問。NET EXEメモリフットプリントを確認することをお勧めします。
  2. MSDNブログの投稿ワーキングセット!=実際のメモリフットプリントは、ワーキングセット、プロセスメモリ、および実行方法を分かりやすく説明するものです。 RAMの総消費量の正確な計算。

アプリケーションのメモリフットプリントを無視する必要があるとは言いません。明らかに、より小さく、より効率的なものが望ましい傾向があります。ただし、実際のニーズを考慮する必要があります。

個人のPCで実行する予定の標準のWindows FormsおよびWPFクライアントアプリケーションを作成しており、ユーザーが操作する主要なアプリケーションである可能性が高い場合は、メモリの割り当てについてより不十分なことを回避できます。 (すべての割り当てが解除される限り。)

ただし、ここでは心配しないと言っている人々に対応するために、ターミナルサービス環境で実行されるWindows Formsアプリケーションを、おそらく10、20人以上のユーザーが使用する共有サーバーで作成する場合は、はい、絶対にメモリ使用量を考慮する必要があります。そして、あなたは用心する必要があります。これに対処する最良の方法は、適切なデータ構造設計を使用し、いつ、何を割り当てるかに関するベストプラクティスに従うことです。

32
John Rudy

.NETアプリケーションは、プロセス内でランタイムとアプリケーションの両方をロードする必要があるため、ネイティブアプリケーションに比べてフットプリントが大きくなります。本当にきちんとしたものが必要な場合は、.NETが最良の選択肢ではないかもしれません。

ただし、アプリケーションの大部分がスリープ状態の場合、必要なメモリページはメモリからスワップアウトされるため、ほとんどの場合、システムにそれほど負担をかけることはありません。

フットプリントを小さく保ちたい場合は、メモリ使用量を考慮する必要があります。ここにいくつかのアイデアがあります:

  • オブジェクトの数を減らし、必要以上に長くインスタンスを保持しないようにしてください。
  • List<T>および必要に応じて容量を2倍にする同様のタイプで、最大50%の無駄が生じる可能性があります。
  • 参照型よりも値型を使用してスタックにより多くのメモリを強制することを検討できますが、デフォルトのスタック領域はわずか1 MBであることに注意してください。
  • 85000バイトを超えるオブジェクトは避けてください。それらは圧縮されていないLOHに送られるため、簡単に断片化される可能性があります。

これはおそらく、決して完全なリストではなく、いくつかのアイデアです。

44
Brian Rasmussen

この場合に考慮する必要があることの1つは、CLRのメモリコストです。 CLRは.Netプロセスごとにロードされるため、メモリに関する考慮事項に影響します。このようなシンプルで小さなプログラムの場合、CLRのコストがメモリフットプリントを支配します。

実際のアプリケーションを構築し、このベースラインプログラムのコストと比較して、そのコストを表示する方がはるかに有益です。

16
JaredPar

特定の提案自体はありませんが、 CLR Profiler (Microsoftからの無料ダウンロード)をご覧ください。
インストールしたら、これをご覧ください how-to page

ハウツーから:

このHow Toでは、CLR Profilerツールを使用して、アプリケーションのメモリ割り当てプロファイルを調査する方法を示します。 CLRプロファイラーを使用して、メモリリークや過剰または非効率的なガベージコレクションなど、メモリの問題を引き起こすコードを特定できます。

7
Donut

「実際の」アプリケーションのメモリ使用量を調べたい場合があります。

Javaと同様に、プログラムサイズに関係なくランタイムのオーバーヘッドは一定の量ですが、メモリ消費はその時点以降ははるかに合理的になります。

6
Eric Petroelje

この単純なプログラムのプライベートワーキングセットを減らす方法はまだあります。

  1. アプリケーションをNGENします。これにより、プロセスからJITコンパイルコストが削除されます。

  2. MPGO メモリ使用量の削減 を使用してアプリケーションをトレーニングし、次にNGENを実行します。

4
Feng Yuan

フットプリントを減らす方法はたくさんあります。

。NET で常に共存しなければならないことの1つは、ILコードのネイティブイメージのサイズがhuge

また、このコードをアプリケーションインスタンス間で完全に共有することはできません。 NGEN'ed アセンブリでも完全に静的ではなく、JITを必要とする小さな部品がいくつか残っています。

また、人々は必要以上にメモリをブロックするコードを書く傾向があります。

よく見られる例:Datareaderを取得し、XMLファイルに書き込むためだけにDataTableにコンテンツをロードします。 OutOfMemoryExceptionに簡単に遭遇できます。 OTOH、XmlTextWriterを使用してDatareaderをスクロールし、データベースカーソルをスクロールするときにXmlNodeを発行できます。これにより、現在のデータベースレコードとそのXML出力のみがメモリに保存されます。ガベージコレクションの生成が高くなることはない(または起こりそうもない)ため、再利用できます。

同じことは、いくつかのインスタンスのリストの取得、いくつかの処理(どこかで参照され続ける可能性のある数千の新しいインスタンスを生成する)にも当てはまります。入力リストと一時的な副産物を明示的にnullにすると、ループを終了する前でもこのメモリを再利用できます。

C#には、イテレーターと呼ばれる優れた機能があります。入力をスクロールすることでオブジェクトをストリーミングでき、次のインスタンスを取得するまで現在のインスタンスのみを保持します。 LINQを使用しても、フィルターを適用したいという理由だけですべてを維持する必要はありません。

2
Robert Giesecke

特定の質問ではなく、タイトルの一般的な質問に対処する:

多くのデータを返すCOMコンポーネント(たとえば、倍精度の大きな2xN配列)を使用していて、ほんの少しだけが必要な場合、.NETからメモリを隠し、データのみを返すラッパーCOMコンポーネントを書くことができます。必要です。

それが私のメインアプリケーションでやったことであり、メモリ消費を大幅に改善しました。

1
Peter Mortensen

SetProcessWorkingSetSizeまたはEmptyWorkingSet APIを使用して、長時間実行されるプロセスでメモリページを定期的にディスクに強制すると、マシンが再起動されるまで、マシン上の使用可能なすべての物理メモリが事実上消えてしまうことがわかりました。 .NET DLL EmptyProcessingSet API(SetProcessWorkingSetSizeを使用する代わり))をネイティブプロセスにロードして、メモリを集中的に使用するタスクを実行した後、ワーキングセットを削減しました。 1日から1週間の間、タスクマネージャでマシンの物理メモリ使用率が99%と表示されますが、大きなメモリ使用量を使用しているプロセスは表示されませんでした。物理ハードウェアと仮想ハードウェアの両方で実行されているWindows Server 2008 R2および2012 R2サーバー。

おそらく、.NETコードをネイティブプロセスにロードすることは、それと関係がありますが、EmptyWorkingSet(またはSetProcessWorkingSetSize)をご自身の責任で使用してください。おそらく、アプリケーションの最初の起動後に一度だけ使用します。コードを無効にして、ガベージコレクターがメモリ使用量を独自に管理できるようにすることにしました。

0
Stuart Welch