6

メモリリークが非常に遅いサービスがあります。.NET CLRの読み込みカウンターを分析すると、現在のクラスの読み込みカウンターが常に増加しており、常に合計のクラスの読み込みカウンターと一致していることがわかります。これは、メモリリークがリソースが解放されていないことに関連しているという印象を与えます(これは単なる推測です)。

このサービスは、タスク(プラグインアーキテクチャ)を実行するたびに新しいappDomainsを作成します。

リークの原因を絞り込むために、クラス名を把握する必要があります。私はWinDbgにあまり精通していませんが、誰かがこれらのLoadedクラスを列挙する手順を説明してくれるかどうか疑問に思っていました。

ソースコードを持っているので、必要に応じてシンボルファイルを入手できます。助けてくれてありがとう!

4

9 に答える 9

2

これは.net2.0以降ですか?もしそうなら、あなたはAppDomain(Jon Skeetがコメントで言っているように)荷降ろししていないかもしれません。

1.1以下の場合、AppDomainアンロード機能にバグがあります。AppDomainつまり、が「アンロード」されたときに、メモリを解放したり、リソースを解放したりすることはありません。

(これは.net 2.0で修正されました)

于 2008-12-05T14:27:53.293 に答える
2

AppDomainにロードされているアセンブリをいつでも確認できます。

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
      Console.WriteLine(assembly.FullName);
}

したがって、誤ってアセンブリを間違ったドメインにロードした場合でも、見づらいことはありません。

編集:

WinDgb SOSを使用する場合、サポートされているコマンドを次に示します。あなたが興味を持っている可能性が最も高いのは、DumpDomain、DumpClass、DumpAssembly、EEHeap..です。

于 2008-12-05T16:59:15.593 に答える
2

この問題は、実際には、RemoteTaskRunner MBRO 内にネストされた一連の未処理の FileSystemWatcher インスタンスが原因であったと考えています。メモリ リークを完全に解決したかどうかはまだわかりませんが、違いははっきりとわかります。

FileSystemWatchers が私に問題を引き起こしたのはこれが初めてではないようです。:)

これを手伝ってくれたみんな(特にleppie)に感謝します!

于 2008-12-05T20:44:27.400 に答える
1

「 .NETMemoryProfiler」のような適切なメモリプロファイラーを使用することをお勧めします。http ://memprofiler.com/ 評価で試して、それが役立つツールの種類であるかどうかを確認できます。

これにより、ハードコアなWinDBG/SoSのものよりもはるかに簡単にすべてのライブオブジェクトを見ることができます。

于 2008-12-05T14:23:16.140 に答える
1

他の回答で述べたように、AppDomain.Unload()を使用する必要があります。

ただし、アセンブリを複数の場所、特にプライマリappdomainにロードしないように注意する必要があります。

上記の方法の説明については、AppDomain.Load(AssemblyName)のMSDNドキュメントを参照してください。

また、同じ行で、適切なリモート可能なクラスを使用していますか?そうでなければ、上記は確実に起こります。

于 2008-12-05T14:31:59.510 に答える
1

スザンヌ・クックのブログでこれを読みました。

http://blogs.msdn.com/suzcook/archive/2003/06/12/57169.aspx

Type/Assembly/etc を渡さないようにしてください。インスタンス (MarshalByRefObject 型以外) を元の appdomain に戻します。これを行うと、それらのアセンブリが元の appdomain に読み込まれます。2 つのアプリドメイン間でアプリドメインの設定が異なる場合、それらのアセンブリをそこで読み込めない可能性があります。さらに、アセンブリが正常に読み込まれた場合でも、元のアプリ ドメインでアセンブリが使用されていなくても、対象のアプリ ドメインがアンロードされた後も、アセンブリは読み込まれたままロックされたままになります。

彼女がタイプ/アセンブリ/などを言うとき。彼女は何を入力できますか? 私が尋ねる理由は、タスクの実行後に MarshalByRefObject (RemoteTaskRunner) が DateTime オブジェクトを返すためです。これにより、プラグイン アセンブリがプライマリ appDomain に読み込まれる (そして最終的にメモリ リークが発生する) 可能性はありますか?

于 2008-12-05T16:13:56.823 に答える
1

誰かがメモリリークを報告するたびに、私はいつもこれをそこに投げ出します.2週間忙しかったからです. アプリをデバッグ モードで実行しないでください。.Net 2.0+ でアプリをデバッグ モードで実行し (.Net 1.1 では発生しませんでした)、イベントを発生させなくても、イベントを含むクラスをインスタンス化すると、小さな値しか保持されません。思い出の一枚。これは、サービスや Web アプリなどの長時間実行されるアプリに大きな影響を与える可能性があります。これは、オブジェクトをインスタンス化した後に消費される少量のメモリが、時間の経過とともにかなりの量になる可能性があるためです。

于 2008-12-05T20:51:00.400 に答える
0

編集:他のいくつかの投稿を読んだ後、より良いプロファイラーが使用された後、およびappDomainの問題が解決された後、これらを考慮に入れる必要があります。

少なくとも作成したオブジェクトを追跡できるように、サービスにパフォーマンスカウンターを追加することをお勧めします。これは、Classes.Loadedが自分のものかCLRクラスかを判断するのに役立ちます。

また、オブジェクトを明示的に作成および破棄する場合のデバッグログを追加すると役立つ場合があります。

最後の手段として、GC.Collect()をそこに配置して、それを呼び出すと問題が修正されるかどうかを確認できますこれは修正ではありませんが、テストするとオプションであることがわかります。

于 2008-12-05T14:27:16.087 に答える
0

AppDomainsはアンロードされますが、leppieからの応答により、プラグインアセンブリがプライマリAppDomainとセカンダリAppDomainの両方にロードされているのかどうか疑問に思います。パフォーマンスカウンターを見ると、現在のAppDomainカウントが常に増加しているわけではありません。

アプリケーションは、セカンダリappDomainを作成してから、別のプラグインアセンブリをロードすることになっています。多分いくつかのコードが役立つでしょう:

プライマリappDomainからセカンダリAppDomainを作成します。

AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationName = "RemoteAgentLib";
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyFiles = "true";

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads);

RemoteTaskRunnerを使用して、プラグインをセカンダリappDomainにロードします。

RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
                    Assembly.GetExecutingAssembly().FullName,
                    typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner;
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type);

RemoteTaskRunnerを使用して、セカンダリappDomainでタスクを実行します。

[Serializable]
internal class RemoteTaskRunner : MarshalByRefObject
{
    private ITask m_task;

    public RemoteTaskRunner()
    {
    }

    internal void LoadTask(string assembly, string type)
    {
        // This assembly should load in the secondary appDomain.
        Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly);
        m_task = taskAssembly.CreateInstance(type) as ITask;
    }

    internal void RunTask(string taskConfig)
    {
        // This method should run in the secondary appDomain.
        m_task.RunTask(taskConfig, m_logger);
    }
...
...

プラグインタスクを実行するために、プライマリappDomainで次のコード行が使用されます。

taskRunner.RunTask(taskInfo.TaskConfig);

タスクが終了すると、appDomainがアンロードされます。

AppDomain.Unload(m_domain);
于 2008-12-05T15:50:27.353 に答える