私は10年間C++を書きました。メモリの問題が発生しましたが、合理的な労力で修正できました。
ここ数年、私はC#を書いてきました。まだ多くのメモリの問題が発生しています。非決定性のため、診断と修正が困難です。また、C#の哲学は、非常に確実に行う場合にそのようなことを心配する必要はないということです。
特定の問題の1つは、コード内のすべてを明示的に破棄およびクリーンアップする必要があることです。そうしないと、メモリプロファイラはあまり助けになりません。なぜなら、あなたが表示しようとしているすべてのデータ内でリークを見つけることができないということです。私は間違った考えを持っているのか、それとも私が持っているツールが最良ではないのかと思います。
.NETでメモリリークに対処するには、どのような戦略とツールが役立ちますか?
メモリリークが疑われる場合、Scitechの MemProfiler を使用します。
これまでのところ、非常に信頼性が高く強力であることがわかりました。少なくとも一度はベーコンを節約できました。
GCは.NET IMOで非常によく機能しますが、他の言語やプラットフォームと同様に、悪いコードを書くと悪いことが起こります。
廃棄を忘れる問題のために、 このブログ投稿で説明されている解決策 を試してください。ここに本質があります:
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
このプロジェクトでは、Red Gateソフトウェアの Ants Profiler Pro を使用しました。これは、すべての.NET言語ベースのアプリケーションで非常にうまく機能します。
.NETガベージコレクターは、インメモリオブジェクトのクリーンアップにおいて(必要に応じて)非常に「安全」であることがわかりました。 might将来的にそれを使用しているという理由だけでオブジェクトを保持します。これは、メモリ内で膨らませたオブジェクトの数にもっと注意する必要があることを意味しました。最終的に、メモリオーバーヘッドを削減し、パフォーマンスを向上させるために、すべてのデータオブジェクトを「オンデマンドで膨張」(フィールドが要求される直前)に変換しました。
編集:ここでは、「オンデマンドで膨らませる」とはどういう意味かをさらに説明します。データベースのオブジェクトモデルでは、親オブジェクトのプロパティを使用して子オブジェクトを公開します。たとえば、他の「詳細」または「ルックアップ」レコードを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メソッドにアクセスするまで)オブジェクトがメモリに保持されないため、はるかに効率的であることがわかりました。これにより、データベースのヒットを制限するパフォーマンスが大幅に向上し、メモリスペースが大幅に増加しました。
アプリケーションが簡単でない限り、マネージコードを記述するときにメモリについて心配する必要があります。私は2つのことを提案します:最初に、読んでください C#経由のCLR これは、.NETのメモリ管理を理解するのに役立つからです。第二に、次のようなツールを使用することを学ぶ CLRProfiler (Microsoft)。これにより、メモリリークの原因を知ることができます(たとえば、大きなオブジェクトヒープの断片化を調べることができます)
アンマネージコードを使用していますか?マイクロソフトによると、アンマネージコードを使用していない場合、従来の意味でのメモリリークは発生しません。
ただし、アプリケーションで使用されているメモリは解放されない可能性があるため、アプリケーションのメモリ割り当ては、アプリケーションの存続期間を通じて増加する可能性があります。
から Microsoft.comの共通言語ランタイムでメモリリークを識別する方法
アプリケーションの一部としてアンマネージコードを使用すると、.NET Frameworkアプリケーションでメモリリークが発生する可能性があります。このアンマネージコードはメモリをリークする可能性があり、.NET Frameworkランタイムはその問題に対処できません。
さらに、プロジェクトにはメモリリークがあるように見える場合があります。この条件は、多数の大きなオブジェクト(DataTableオブジェクトなど)が宣言され、コレクション(DataSetなど)に追加された場合に発生する可能性があります。これらのオブジェクトが所有するリソースは決して解放されない可能性があり、リソースはプログラムの実行全体にわたって生存します。これはリークのように見えますが、実際には、プログラムでメモリが割り当てられている方法の単なる症状です。
このタイプの問題に対処するには、 IDisposable を実装できます。メモリ管理を扱うための戦略のいくつかを確認したい場合は、ゲーム開発者が必要とするIDisposable、XNA、メモリ管理を検索することをお勧めしますより予測可能なガベージコレクションであるため、GCにその処理を強制する必要があります。
よくある間違いの1つは、オブジェクトをサブスクライブするイベントハンドラーを削除しないことです。イベントハンドラサブスクリプションは、オブジェクトのリサイクルを防ぎます。また、リソースの存続期間に対して限定されたスコープを作成できる sing ステートメントを見てください。
このブログ には、windbgやその他のツールを使用してすべての種類のメモリリークを追跡する、いくつかの非常に素晴らしいチュートリアルがあります。スキルアップのための優れた読書。
Windowsサービスでメモリリークが発生したのを修正しました。
最初に、 MemProfiler を試しました。使いにくいことがわかりましたが、ユーザーフレンドリーではありませんでした。
次に、 JustTrace を使用しました。これは使いやすく、正しく破棄されていないオブジェクトに関する詳細を提供します。
メモリリークを簡単に解決できました。
大きな銃- Windows用デバッグツール
これは素晴らしいツールのコレクションです。管理ヒープと非管理ヒープの両方を分析でき、オフラインで実行できます。これは、メモリの過剰使用のためにリサイクルを続けていたASP.NETアプリケーションの1つをデバッグするのに非常に便利でした。実稼働サーバーで実行されている生きているプロセスの完全なメモリダンプを作成するだけで、すべての分析はWinDbgでオフラインで行われました。 (一部の開発者はメモリ内のセッションストレージを使いすぎていたことが判明しました。)
「壊れている場合...」 ブログには、このテーマに関する非常に有用な記事があります。
観察しているリークが暴走したキャッシュ実装によるものである場合、これはmight WeakReferenceの使用を検討したいシナリオです。これにより、必要なときにメモリを確実に解放できます。
しかし、私見では、オーダーメイドのソリューションを検討する方が良いでしょう-オブジェクトを保持する必要がある期間を本当に知っているのはあなただけなので、状況に応じて適切なハウスキーピングコードを設計するのが通常最良のアプローチです。
心に留めておくべき最善のことは、オブジェクトへの参照を追跡することです。気にしないオブジェクトへの参照をぶら下げてしまうのは非常に簡単です。何かを使用しない場合は、それを取り除きます。
スライディング有効期限付きのキャッシュプロバイダーの使用に慣れて、目的の時間枠で何かが参照されない場合は、参照が解除され、クリーンアップされるようにします。しかし、それが頻繁にアクセスされている場合、それはメモリ内で言うでしょう。
マネージアプリケーションの修正後、次の変更後にアプリケーションに同じメモリリークが発生しないことを確認する方法など、同じことがありました。そのため、Object Release Verificationフレームワークなどを作成しました。 NuGetパッケージ ObjectReleaseVerification 。サンプルはこちらにあります https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample 、およびこのサンプルに関する情報 http://outcoldman.ru/en/blog/ show/322
最適なツールの1つは、 Debugging Tools for Windows を使用し、adplusを使用してプロセスのメモリダンプを取得することです。 windbgおよびsosプラグインを使用して、プロセスメモリ、スレッド、呼び出しスタック。
この方法を使用してサーバー上の問題を特定し、ツールをインストールした後、ディレクトリを共有し、(Net Use)を使用してサーバーから共有に接続し、プロセスのクラッシュまたはハングダンプを取得できます。
次に、オフラインで分析します。
dotmemory Jetbrainsから
Visual Studio 2015から、すぐに使用できることを考慮してください メモリ使用量診断ツール は、メモリ使用量データを収集および分析します。
メモリ使用量ツールを使用すると、管理対象およびネイティブのメモリヒープのスナップショットを1つ以上取得して、オブジェクトタイプのメモリ使用量への影響を理解できます。