11

Enterキーを押して次のコントロールに移動できるようにする「アタッチされた動作」をWPFアプリケーションで作成しました。私はこれを EnterKeyTraversal.IsEnabled と呼んでいます。コードはこちらのブログでご覧いただけます。

私の主な懸念は、UIElements で PreviewKeyDown イベントを処理していて、イベントを明示的に「フック解除」することがないため、メモリ リークが発生する可能性があることです。

このリークを防ぐための最善の方法は何ですか (実際にリークがある場合)? 管理している要素のリストを保持し、Application.Exit イベントで PreviewKeyDown イベントをアンフックする必要がありますか? 独自の WPF アプリケーションでアタッチされた動作に成功し、洗練されたメモリ管理ソリューションを思いついた人はいますか?

4

11 に答える 11

5

私はDannySmurfに同意しません

一部のWPFレイアウトオブジェクトは、ガベージコレクションされていない場合、メモリを詰まらせ、アプリケーションを非常に遅くする可能性があります。ですから、正しい単語の選択を見つけました。使用しなくなったオブジェクトにメモリリークが発生しています。アイテムがガベージコレクションされることを期待しますが、どこかに参照があるため(この場合はイベントハンドラーから)、そうではありません。

今本当の答えのために:)

MSDNのこのWPFパフォーマンスの記事を読むことをお勧めします

オブジェクトのイベントハンドラーを削除しないと、オブジェクトが存続する可能性があります

オブジェクトがそのイベントに渡すデリゲートは、事実上、そのオブジェクトへの参照です。したがって、イベントハンドラーは、オブジェクトを予想よりも長く存続させることができます。オブジェクトのイベントをリッスンするように登録されているオブジェクトのクリーンアップを実行するときは、オブジェクトを解放する前にそのデリゲートを削除することが不可欠です。不要なオブジェクトを存続させると、アプリケーションのメモリ使用量が増加します。これは、オブジェクトが論理ツリーまたはビジュアルツリーのルートである場合に特に当てはまります。

彼らはあなたに弱いイベントパターンを調べるようにアドバイスします

もう1つの解決策は、オブジェクトの処理が完了したら、イベントハンドラーを削除することです。しかし、添付プロパティでは、その点が必ずしも明確ではない可能性があることを私は知っています。

お役に立てれば!

于 2008-08-18T08:39:35.507 に答える
5

哲学的な議論はさておき、OPのブログ投稿を見ると、ここに漏れは見られません。

ue.PreviewKeyDown += ue_PreviewKeyDown;

へのハード参照ue_PreviewKeyDownが に格納されていue.PreviewKeyDownます。

ue_PreviewKeyDownSTATICメソッドであり、 にすることはできませんGCed

へのハード参照ueは保存されていないため、GCed.

それで... 漏れはどこですか?

于 2011-05-29T18:56:32.340 に答える
4

はい、昔はメモリ リークがまったく別の話題だったことを私は知っています。しかし、マネージド コードでは、メモリ リークという用語の新しい意味の方が適切かもしれません...

マイクロソフトは、それがメモリ リークであることを認めています。

WeakEvent パターンを実装する理由

イベントをリッスンすると、メモリ リークが発生する可能性があります。イベントをリッスンする一般的な手法は、ソースのイベントにハンドラーをアタッチする言語固有の構文を使用することです。たとえば、C# の構文は、source.SomeEvent += new SomeEventHandler(MyEventHandler) です。

この手法により、イベント ソースからイベント リスナーへの強力な参照が作成されます。通常、リスナーにイベント ハンドラーをアタッチすると、ソースのオブジェクトの有効期間に影響されるオブジェクトの有効期間がリスナーに設定されます (イベント ハンドラーが明示的に削除されない限り)。ただし、特定の状況では、リスナーのオブジェクトの有効期間を、ソースの有効期間ではなく、現在アプリケーションのビジュアル ツリーに属しているかどうかなど、他の要因によってのみ制御する必要がある場合があります。ソース オブジェクトの有効期間がリスナーのオブジェクトの有効期間を超えると、通常のイベント パターンではメモリ リークが発生します。つまり、リスナーが意図したよりも長く存続します。

ドラッグ ドロップできる大きな ToolWindows、すべての気の利いたもの、すべて XBAP と互換性のあるクライアント アプリに WPF を使用します。まだイベントリスナーに依存していたという事実に..ウィンドウを閉じてアプリをシャットダウンしても、これは問題にならないかもしれません。しかし、多くのコマンドを含む非常に大きな ToolWindows を作成していて、これらすべてのコマンドが何度も何度も再評価され、人々があなたのアプリケーションを 1 日中使用しなければならない場合..私はあなたに言うことができます..それは本当にあなたのメモリを詰まらせます.アプリの応答時間..

また、クリーニングが必要なイベントのために一部のオブジェクトがガベージ コレクションされないことをマネージャーに説明するよりも、メモリ リークがあることをマネージャーに説明する方がはるかに簡単だと思います ;)

于 2008-08-18T14:44:49.763 に答える
3

@ニックええ、ビヘイビアが添付されていることは、定義により、イベントを処理している要素と同じオブジェクトにないということです。

答えは何らかの方法で WeakReference を使用することにあると思いますが、それを説明するための簡単なコード サンプルは見たことがありません。:)

于 2008-08-18T01:51:27.593 に答える
2

ここでのジョン・フェントンの投稿に対する私のコメントを説明するのが私の答えです。次の例を見てみましょう。

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();

        a.Clicked += b.HandleClicked;
        //a.Clicked += B.StaticHandleClicked;
        //A.StaticClicked += b.HandleClicked;

        var weakA = new WeakReference(a);
        var weakB = new WeakReference(b);

        a = null;
        //b = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Console.WriteLine("a is alive: " + weakA.IsAlive);
        Console.WriteLine("b is alive: " + weakB.IsAlive);
        Console.ReadKey();
    }


}

class A
{
    public event EventHandler Clicked;
    public static event EventHandler StaticClicked;
}

class B
{
    public void HandleClicked(object sender, EventArgs e)
    {
    }

    public static void StaticHandleClicked(object sender, EventArgs e)
    {
    }
}

あなたが持っている場合

a.Clicked += b.HandleClicked;

b のみを null に設定すると、weakA と weakB の両方の参照が有効になります。a のみを null に設定すると、b は存続しますが、a は存続しません (これは、ハード参照がイベント プロバイダー (この場合は a) に格納されていると述べた John Fenton が間違っていることを証明しています)。

これは私を間違った結論に導きました

a.Clicked += B.StaticHandleClicked;

のインスタンスが静的ハンドラーによって保持されるため、リークが発生する可能性があります。これは当てはまりません (私のプログラムをテストしてください)。静的イベント ハンドラーまたはイベントの場合は、逆になります。あなたが書くなら

A.StaticClicked += b.HandleClicked;

b への参照が保持されます。

于 2015-01-30T15:33:53.160 に答える
1

Have you though of implementing the "Weak Event Pattern" instead of regular events?

  1. Weak Event Pattern in WPF
  2. Weak Event Patterns (MSDN)
于 2008-08-18T08:48:25.473 に答える
0

ブログの投稿を読んだところ、誤解を招くようなアドバイスが少しあったと思います、マット。ここに実際のメモリリークがある場合、それは.NET Frameworkのバグであり、コードで必ずしも修正できるものではありません。

あなた(そしてあなたのブログのポスター)が実際にここで話しているのは、実際にはリークではなく、継続的なメモリの消費です。それは同じことではありません。明確にするために、リークされたメモリは、プログラムによって予約され、その後放棄された(つまり、ポインタがぶら下がったままになっている)メモリであり、その後解放することはできません。メモリは.NETで管理されるため、これは理論的には不可能です。ただし、プログラムが、参照をスコープ外にすることなく(そしてガベージコレクションの対象となることなく)、増え続けるメモリ量を予約することは可能です。ただし、そのメモリはリークされません。プログラムが終了すると、GCはそれをシステムに返します。

それで。あなたの質問に答えるために、私はあなたが実際にここで問題を抱えているとは思わない。確かにメモリリークはありません。コードからすると、メモリ消費量についても心配する必要はないと思います。割り当てを解除せずにそのイベントハンドラーを繰り返し割り当てないことを確認する限り(つまり、一度だけ設定するか、割り当てるたびに1回だけ削除する)、あなたがやっているようです、あなたのコードはうまくいくはずです。

それはあなたのブログのポスターがあなたに与えようとしていたアドバイスのようですが、彼はその驚くべき作品「リーク」を使用しました。これは恐ろしい言葉ですが、多くのプログラマーは管理された世界での本当の意味を忘れています。ここでは適用されません。

于 2008-08-18T03:40:18.403 に答える
0

フォーム コントロールのテキスト ボックスのように、イベント参照要素が参照しているオブジェクト内にあることを確認してください。または、それを防ぐことができない場合。グローバル ヘルパー クラスで静的イベントを作成し、グローバル ヘルパー クラスでイベントを監視します。これらの 2 つの手順を実行できない場合は、WeakReference を使用してみてください。これらの手順は通常、これらの状況に最適ですが、オーバーヘッドが伴います。

于 2008-08-18T00:58:04.283 に答える
0

@アークトゥルス:

... ガベージ コレクションが行われていないと、メモリが詰まり、アプリケーションが非常に遅くなります。

それは盲目的に明白であり、私は反対しません。でも:

...使用しなくなったオブジェクトにメモリをリークしています...それらへの参照があるためです。

「メモリがプログラムに割り当てられ、そのプログラムはその後、プログラム ロジックの欠陥によりメモリにアクセスできなくなります」(ウィキペディア、「メモリ リーク」)

プログラムがアクセスできるオブジェクトへのアクティブな参照がある場合、定義上、メモリ リークは発生しません。リークとは、オブジェクトが (ユーザーまたは OS/フレームワークから) アクセスできなくなり、オペレーティング システムの現在のセッションが続く限り解放されないことを意味します。ここではそうではありません。

(セマンティック ナチで申し訳ありません... 私は少し古い学校かもしれませんが、リークには非常に具体的な意味があります。最近では、必要以上に 2KB のメモリを消費するものを意味するために "メモリ リーク" を使用する傾向があります。 ..)

しかしもちろん、イベント ハンドラーを解放しないと、イベント ハンドラーがアタッチされているオブジェクトは、シャットダウン時にガベージ コレクターによってプロセスのメモリが回収されるまで解放されません。しかし、あなたが暗示しているように見えるものとは反対に、この動作は完全に期待されています。オブジェクトが再利用されることが予想される場合は、イベント ハンドラーなど、参照を維持する可能性のあるものをすべて削除する必要があります。

于 2008-08-18T14:27:48.127 に答える
0

まさしく、

もちろんあなたの言う通りです..しかし、アンマネージ コードに触れることのないまったく新しい世代のプログラマーがこの世界に生まれつつあります。私は、言語定義が何度も再発明されると信じています。このように、WPF でのメモリ リークは、C/Cpp とは異なります。

またはもちろん、マネージャーにはメモリリークと呼びました..同僚にはパフォーマンスの問題と呼びました!

マットの問題を参照すると、取り組む必要があるパフォーマンスの問題である可能性があります。いくつかのスクリーンを使用し、それらのスクリーン コントロールをシングルトンにすると、この問題はまったく見られないかもしれません ;)。

于 2008-08-18T15:20:39.800 に答える
-2

まあ、それは(マネージャーのビット)確かに理解できますし、共感できます。

しかし、Microsoft が何と呼んでいようと、「新しい」定義は適切ではないと思います。私たちは 100% 管理された世界に住んでいるわけではないので、複雑です (Microsoft は私たちが管理されているふりをしたがりますが、Microsoft 自体はそのような世界に住んでいません)。メモリ リークと言う場合、プログラムが大量のメモリを消費している (これはユーザーの定義です)、マネージ参照が終了するまで解放されない (ここのように)、またはアンマネージ参照が適切に消去されていないことを意味する可能性があります。アップ (実際のメモリ リーク)、またはマネージド コードから呼び出されたアンマネージド コードがメモリ リーク (別の実際のリーク) です。

この場合、不正確ではありますが、「メモリ リーク」が何を意味するかは明らかです。しかし、すべての過剰消費または収集の失敗をメモリリークと呼ぶ一部の人々と話すのは非常に退屈です。そして、これらの人々がおそらくよりよく知っていると思われるプログラマーであるとき、それはイライラします. 専門用語の意味が曖昧でないことは重要なことだと思います。デバッグは非常に簡単です。

ともかく。これを言語についての風通しの良いおとぎ話に変えるつもりはありません。ただ言って...

于 2008-08-18T15:09:55.577 に答える