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


108

.NETアプリケーションのメモリ使用量を減らすためのヒントは何ですか?次の単純なC#プログラムについて考えてみます。

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

タスクマネージャーは、x64のリリースモードでコンパイルされ、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外のx86 リリースの結果:

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

これは少し良いですが、そのような単純なプログラムではまだ過剰に見えます。C#プロセスを少しスリムにするコツはありますか?ほとんどの場合バックグラウンドで実行するように設計されたプログラムを書いています。私はすでに別のアプリケーションドメインでユーザーインターフェイスを実行しています。つまり、ユーザーインターフェイスは安全にアンロードできますが、バックグラウンドに置かれているだけで10 MBを消費することは過剰に思えます。

PSなぜ私が気にするのか---(パワー)ユーザーはこれらのことを心配する傾向があります。パフォーマンスにほとんど影響がないとしても、セミテクノロジーに精通しているユーザー(私の対象読者)は、バックグラウンドアプリケーションのメモリ使用量について、ひどく当てはまる傾向があります。Adobe Updaterが11 MBのメモリを使用していて、Foobar2000の落ち着いたタッチに癒されているのを見ると、私はさらに驚かされます。現代のオペレーティングシステムでは、このことは技術的にそれほど重要ではありませんが、知覚に影響がないという意味ではありません。


14
なぜあなたは気にしますか?プライベートワーキングセットはかなり低いです。メモリが不要な場合、最新のOSはディスクにページアウトします。それは2009年です。組み込みシステムでものを構築しているのでない限り、10MBは気にする必要はありません。
Mehrdad Afshari、

8
.NETの使用を停止すると、小さなプログラムを作成できます。.NETフレームワークをロードするには、多くの大きなDLLをメモリにロードする必要があります。
Adam Sills、

2
メモリ価格は指数関数的に低下します(はい、24 GB RAMのDell家庭用コンピュータシステムを今すぐ注文できます)。アプリケーションで500MBを超える場合を除き、最適化は不要です。
アレックス

28
@LeakyCode私は現代のプログラマーがこのように考えるのが本当に嫌いです、あなたはあなたのアプリケーションのメモリ使用量に注意すべきです。リソース管理に関しては、ほとんどがJavaまたはC#で記述された最新のアプリケーションのほとんどはかなり効果がなく、そのおかげで、2014年には、Win95および64MBのRAMで1998年にできる限り多くのアプリケーションを実行できます...ブラウザの1つのインスタンスが2GBのRAMとシンプルなIDEを約1GB消費します。ラムは安いですが、それはあなたがそれを無駄にすべきだという意味ではありません。
Petr

6
@Petrリソース管理に注意する必要があります。プログラマーの時間もリソースです。10MBから2GBを無駄に使いすぎないようにしてください。
Mehrdad Afshari 2014年

回答:


33
  1. Stack Overflowの質問.NET EXEのメモリフットプリントを確認してください。
  2. MSDNブログの投稿ワーキングセット!=実際のメモリフットプリントは、ワーキングセット、プロセスメモリ、およびRAM内の合計消費量を正確に計算する方法をわかりやすく説明しています。

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

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

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


45

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

ただし、アプリケーションがほとんどスリープしている場合は、必要なメモリページがメモリからスワップアウトされるため、ほとんどの場合、システムへの負荷はそれほど大きくありません。

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

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

これは、おそらく完全なリストではなく、いくつかのアイデアにすぎません。


IOW、.NETでネイティブコードのサイズを小さくするのに役立つ同じ種類のテクニックは?
Robert Fraser、

いくつかの重複があると思いますが、ネイティブコードを使用すると、メモリの使用に関してより多くの選択肢があります。
ブライアンラスムッセン

17

この場合に考慮する必要がある1つのことは、CLRのメモリコストです。CLRはすべての.Netプロセスに対して読み込まれるため、メモリに関する考慮事項が考慮されます。そのような単純で小さなプログラムの場合、CLRのコストがメモリフットプリントを支配します。

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


7

それ自体は具体的な提案はありませんが、CLRプロファイラー(Microsoftからの無料ダウンロード)をご覧ください。
インストールしたら、このハウツーページをご覧ください

ハウツーから:

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


7

「実際の」アプリケーションのメモリ使用量を見たいと思うかもしれません。

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


4

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

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

  2. MPGOを使用してアプリケーションをトレーニングし、メモリ使用量減らしてから、NGENを使用します。


2

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

.NETで常に使用する必要があることの1つは、ILコードのネイティブイメージのサイズが巨大であることです。

また、このコードをアプリケーションインスタンス間で完全に共有することはできません。NGEN化されたアセンブリでさえ完全に静的ではなく、JITを必要とするいくつかの小さなパーツがまだあります。

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

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

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

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


1

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

大量のデータを返すCOMコンポーネント(たとえば、2倍の大きな2xN配列)を使用していて、ほんの少しだけ必要な場合は、.NETからメモリを隠して、必要。

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


0

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.