155

私は C++ を 10 年間書きました。メモリの問題が発生しましたが、かなりの努力で修正できました。

ここ数年、私は C# を書いています。私はまだ多くのメモリの問題を抱えていることに気づきました。それらは非決定性のために診断と修正が困難であり、C# の哲学は、非常に確実に行う場合はそのようなことを心配する必要はないということです。

私が見つけた特定の問題の 1 つは、コード内のすべてを明示的に破棄してクリーンアップする必要があることです。そうしないと、メモリプロファイラーはあまり役に立ちません。なぜなら、表示しようとしているすべてのデータ内でリークを見つけることができないほど多くのチャフが浮かんでいるからです。私の考えが間違っているのか、それとも私の持っているツールが最適ではないのだろうか。

.NET でのメモリ リークに対処するには、どのような戦略とツールが役立ちますか?

4

15 に答える 15

52

メモリ リークが疑われる場合は、 Scitech のMemProfilerを使用します。

これまでのところ、非常に信頼性が高く強力であることがわかりました。少なくとも 1 回は私のベーコンを救ってくれました。

GC は .NET IMO で非常にうまく機能しますが、他の言語やプラットフォームと同様に、悪いコードを書くと悪いことが起こります。

于 2008-09-25T15:43:44.177 に答える
41

廃棄するのを忘れる問題については、このブログ投稿で説明されている解決策を試してください。本質は次のとおりです。

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif
于 2008-09-25T16:26:38.287 に答える
17

プロジェクトでは Red Gate ソフトウェアのAnts Profiler Proを使用しました。これは、すべての .NET 言語ベースのアプリケーションで非常にうまく機能します。

.NET ガベージ コレクターは、メモリ内オブジェクトのクリーンアップにおいて非常に「安全」であることがわかりました (そうあるべきです)。将来いつか使用する可能性があるという理由だけで、オブジェクトを保持します。これは、メモリ内でインフレートするオブジェクトの数について、より注意を払う必要があることを意味していました。最後に、メモリのオーバーヘッドを減らしてパフォーマンスを向上させるために、すべてのデータ オブジェクトを "inflate on-demand" (フィールドが要求される直前) に変換しました。

編集: 「オンデマンドで膨らませる」という言葉の意味をさらに詳しく説明します。データベースのオブジェクト モデルでは、親オブジェクトのプロパティを使用して子オブジェクトを公開します。たとえば、他の「detail」または「lookup」レコードを 1 対 1 で参照するレコードがある場合、次のように構造化します。

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

上記のシステムでは、メモリ内に多数のレコードがある場合、実際のメモリとパフォーマンスの問題が発生することがわかりました。そこで、オブジェクトが要求されたときにのみインフレートされ、必要なときにのみデータベース呼び出しが行われるシステムに切り替えました。

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

これは、オブジェクトが必要になるまで (Get メソッドがアクセスされるまで) メモリから保持されていたため、はるかに効率的であることが判明しました。これにより、データベース ヒットを制限することでパフォーマンスが大幅に向上し、メモリ スペースが大幅に増加しました。

于 2008-09-25T15:51:09.167 に答える
7

アプリケーションが自明でない限り、マネージ コードを作成するときは、依然としてメモリについて心配する必要があります。2 つのことを提案します。まず、.NET でのメモリ管理を理解するのに役立つため、C# 経由で CLR を読んでください。次に、 CLRProfiler (Microsoft)などのツールの使用方法を学びます。これにより、メモリ リークの原因を突き止めることができます (たとえば、大きなオブジェクト ヒープの断片化を調べることができます)。

于 2008-09-25T15:50:53.053 に答える
6

管理されていないコードを使用していますか?Microsoftによると、アンマネージコードを使用していない場合、従来の意味でのメモリリークは発生しません。

ただし、アプリケーションで使用されているメモリは解放されない場合があるため、アプリケーションのメモリ割り当ては、アプリケーションの存続期間を通じて増大する可能性があります。

Microsoft.comの共通言語ランタイムでメモリリークを特定する方法から

アプリケーションの一部としてアンマネージコードを使用すると、.NETFrameworkアプリケーションでメモリリークが発生する可能性があります。このアンマネージコードはメモリリークを引き起こす可能性があり、.NETFrameworkランタイムはその問題に対処できません。

さらに、プロジェクトにはメモリリークがあるように見えるだけかもしれません。この状態は、多数の大きなオブジェクト(DataTableオブジェクトなど)が宣言されてからコレクション(DataSetなど)に追加された場合に発生する可能性があります。これらのオブジェクトが所有するリソースは決して解放されない可能性があり、リソースはプログラムの実行全体にわたって存続します。これはリークのように見えますが、実際には、プログラムでメモリが割り当てられている方法の症状にすぎません。

このタイプの問題に対処するために、IDisposableを実装できます。メモリ管理を処理するための戦略のいくつかを確認したい場合は、ゲーム開発者がより予測可能なガベージコレクションを行う必要があり、GCにその処理を強制する必要があるため、IDisposable、XNA、メモリ管理を検索することをお勧めします。

よくある間違いの1つは、オブジェクトをサブスクライブするイベントハンドラーを削除しないことです。イベントハンドラサブスクリプションは、オブジェクトのリサイクルを防ぎます。また、リソースの存続期間に限定されたスコープを作成できるusingステートメントも確認してください。

于 2008-09-25T16:41:01.857 に答える
5

このブログには、windbgやその他のツールを使用して、あらゆる種類のメモリリークを追跡するための非常にすばらしいウォークスルーがいくつかあります。あなたのスキルを伸ばすための優れた読書。

于 2008-10-01T00:47:37.203 に答える
5

Windows サービスでメモリ リークが発生しましたが、これを修正しました。

まず、MemProfilerを試してみました。私はそれが本当に使いにくく、ユーザーフレンドリーではないと感じました。

次に、より使いやすく、正しく破棄されていないオブジェクトの詳細を提供するJustTraceを使用しました。

これにより、メモリリークを非常に簡単に解決できました。

于 2015-10-22T15:03:29.867 に答える
3

ビッグガン-Windows用のデバッグツール

これは素晴らしいツールのコレクションです。これを使用して、マネージヒープとアンマネージヒープの両方を分析でき、オフラインで実行できます。これは、メモリの過剰使用のためにリサイクルを続けていたASP.NETアプリケーションの1つをデバッグするのに非常に便利でした。本番サーバーで実行されている稼働中のプロセスの完全なメモリダンプを作成するだけで済み、すべての分析はWinDbgでオフラインで実行されました。(一部の開発者がメモリ内のセッションストレージを使いすぎていたことが判明しました。)

「壊れたら…」ブログには、このテーマに関する非常に役立つ記事があります。

于 2008-09-25T16:44:27.323 に答える
3

観察しているリークがキャッシュの実装の暴走によるものである場合、これはWeakReferenceの使用を検討する必要があるシナリオです。これは、必要なときにメモリが確実に解放されるようにするのに役立ちます。

ただし、私見では、特注のソリューションを検討することをお勧めします。オブジェクトを保持する必要がある期間を実際に知っているのはあなただけなので、通常、状況に適したハウスキーピングコードを設計することが最善のアプローチです。

于 2008-09-25T16:22:09.920 に答える
2

最高のツールの1つは、Windows用のデバッグツールを使用し、 adplusを使用してプロセスのメモリダンプを取得し、windbgsosプラグインを使用してプロセスメモリ、スレッド、およびコールスタックを分析することです。

この方法を使用してサーバーの問題を特定することもできます。ツールをインストールした後、ディレクトリを共有し、(net use)を使用してサーバーから共有に接続し、プロセスのクラッシュまたはハングダンプを取得します。

次に、オフラインで分析します。

于 2008-09-25T16:12:48.397 に答える
2

覚えておくべき最良のことは、オブジェクトへの参照を追跡することです。気にしないオブジェクトへの参照がぶら下がってしまうのは非常に簡単です。もう何かを使用する予定がない場合は、それを取り除きます。

有効期限がスライドするキャッシュプロバイダーの使用に慣れてください。これにより、目的の時間枠で参照されていないものが逆参照され、クリーンアップされます。しかし、それが頻繁にアクセスされている場合、それはメモリ内に表示されます。

于 2008-09-25T15:55:53.960 に答える
2

マネージド アプリケーションの修正の 1 つの後、次の変更後にアプリケーションに同じメモリ リークが発生しないことを確認する方法など、同じことがありました。そのため、オブジェクト リリース検証フレームワークのようなものを書きました。 NuGet パッケージObjectReleaseVerification。ここでサンプルを見つけることができますhttps://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample、およびこのサンプルに関する情報http://outcoldman.ru/en/blog/show/322

于 2012-09-17T04:01:37.843 に答える