سؤال

لقد خلق "تعلق السلوك" في تطبيق WPF الذي يتيح لي التعامل مع إدخال keypress و الانتقال إلى عنصر التحكم التالي.أنا أسميها EnterKeyTraversal.IsEnabled و يمكنك أن ترى رمز على بلدي بلوق هنا.

قلقي الرئيسي الآن هو أنني قد تسرب الذاكرة, منذ أنا التعامل مع PreviewKeyDown الحدث على UIElements أبدا صراحة "فك" هذا الحدث.

ما هو أفضل نهج لمنع هذا التسرب (إذا كان هناك واحد)?يجب أن تبقى قائمة من العناصر انا وإدارة نزع من الخطاف PreviewKeyDown الحدث في التطبيق.خروج الحدث ؟ وقد أي شخص كان النجاح المرفقة مع السلوكيات الخاصة في تطبيقات WPF و تأتي مع أنيقة إدارة الذاكرة الحل ؟

هل كانت مفيدة؟

المحلول

أنا لا أتفق DannySmurf

بعض WPF تخطيط الكائنات يمكن أن تسد الذاكرة الخاصة بك و جعل التطبيق الخاص بك بطيئة حقا عندما لا يتم جمعها من القمامة.لذلك أجد اختيار الكلمات الصحيحة ، تسرب الذاكرة إلى الأشياء التي لم تعد تستخدمها.تتوقع بنود القمامة التي تم جمعها ، لكنهم ليسوا كذلك لأن هناك إشارة في مكان ما (في هذه الحالة في من معالج الحدث).

الآن على إجابة حقيقية :)

أنصحك أن تقرأ هذا WPF الأداء المادة على MSDN

لا إزالة معالجات الأحداث على الكائنات قد تبقى الكائنات على قيد الحياة

المندوب أن يمر كائن إلى الحدث هو فعال إشارة إلى هذا الكائن.لذلك الحدث ، معالجات يمكن أن تبقى الأشياء على قيد الحياة لفترة أطول من المتوقع.عند تنفيذ نظيفة من كائن له مسجلة الاستماع إلى كائن الحدث ، فمن من الضروري إزالة هذا المندوب قبل الإفراج عن الكائن.حفظ غير الضرورية الكائنات على قيد الحياة يزيد التطبيق استخدام الذاكرة.هذا هو خصوصا عندما يكون الهدف هو جذر شجرة منطقية أو البصرية شجرة.

ننصحك أن تنظر إلى ضعيفة الحدث نمط

آخر حل هو إزالة معالجات الأحداث عندما كنت فعلت مع كائن.ولكن أنا أعرف أن المرفقة مع خصائص هذه النقطة قد لا تكون دائما واضحة..

ويساعد هذا الأمل!

نصائح أخرى

الفلسفية النقاش جانبا ، في النظر في المرجع بلوق وظيفة, أنا لا أرى أي تسرب هنا:

ue.PreviewKeyDown += ue_PreviewKeyDown;

بجد إشارة إلى ue_PreviewKeyDown يتم تخزينها في ue.PreviewKeyDown.

ue_PreviewKeyDown هو STATIC طريقة و لا يمكن أن يكون GCed.

لا بجد إشارة إلى ue يتم تخزين, لذلك لا شيء يمنع من أن تكون GCed.

لذا...أين هو التسريب ؟

نعم أنا أعرف أنه في الأيام القديمة تسرب الذاكرة مختلفة كليا عن الموضوع.ولكن مع التعليمات البرمجية المدارة, معنى جديدا لمصطلح تسرب الذاكرة قد تكون أكثر ملاءمة...

مايكروسوفت حتى يعترف أن يكون تسرب الذاكرة:

لماذا تنفيذ WeakEvent النمط ؟

الاستماع الأحداث يمكن أن يؤدي إلى تسرب الذاكرة.نموذجية تقنية من أجل الاستماع إلى حدث هو استخدام لغة محددة الجملة التي تعلق معالج الحدث على المصدر.فعلى سبيل المثال ، في C# ، بناء الجملة هو:المصدر.SomeEvent += new SomeEventHandler(MyEventHandler).

ينشئ هذا الأسلوب القوي إشارة من مصدر الحدث إلى المستمع الحدث.عادة ربط معالج أحداث المستمع الأسباب المستمع أن يكون كائن عمر أن يتأثر الكائن العمر المصدر (ما لم معالج الحدث صراحة إزالتها).ولكن في بعض الظروف قد أريد الكائن عمر المستمع أن يسيطر فقط عوامل أخرى ، مثل ما إذا كان حاليا ينتمي إلى شجرة البصرية من التطبيق ، وليس عمر من المصدر.كلما الكائن المصدر يمتد إلى أبعد مدى الحياة الكائن عمر المستمع ، العادي الحدث نمط يؤدي إلى تسرب الذاكرة:المستمع هو أبقى على قيد الحياة لفترة أطول من الهدف.

ونحن استخدام WPF لعميل التطبيق كبيرة ToolWindows التي يمكن أن تنجر إسقاط جميع الأشياء أنيق و كل متوافق مع XBAP..ولكن كان لدينا نفس المشكلة مع بعض ToolWindows التي لم يتم جمعها من القمامة..ويرجع ذلك إلى حقيقة أنه لا يزال يعتمد على المستمعين الحدث..الآن هذا قد لا يكون مشكلة عند إغلاق النافذة و إيقاف التطبيق الخاص بك.ولكن إذا كنت تقوم بإنشاء كبيرة جدا ToolWindows مع الكثير من الأوامر و جميع هذه الأوامر يحصل على إعادة تقييم مرارا وتكرارا يجب استخدام التطبيق الخاص بك طوال اليوم..أستطيع أن أقول لك..حقا يسد الذاكرة الخاصة بك و وقت استجابة من التطبيق الخاص بك..

كما أجد أنه من الأسهل بكثير أن يشرح مدير أعمالي أن لدينا تسرب الذاكرة ، من يشرح له أن بعض الكائنات لا يتم جمعها من القمامة بسبب بعض الأحداث التي يحتاج التنظيف؛)

@نيك نعم الشيء المرفقة مع السلوكيات هو أن تعريف أنهم ليسوا في نفس موضوع العناصر الأحداث التي أنت المناولة.

أعتقد أن الجواب يكمن في استخدام WeakReference بطريقة أو بأخرى, ولكن أنا لم أر أي رمز بسيط عينات تفسير ذلك بالنسبة لي.:)

هل على الرغم من تنفيذ "ضعيفة الحدث نمط" بدلا من العادية الأحداث ؟

  1. ضعيفة الحدث نمط في WPF
  2. ضعيفة الحدث أنماط (MSDN)التالي

شرح تعليقي على جون فنتون وظيفة هنا هو جوابي.دعنا نرى المثال التالي:

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;

وتعيين فقط ب null كل المراجع weakA و weakB البقاء على قيد الحياة!إذا قمت بتعيين فقط إلى null ب يبقى حيا ولكن لا (وهو ما يثبت أن جون فنتون هو الخطأ مشيرا إلى أن من الصعب الإشارة المخزنة في مقدم الحدث - في هذه الحالة).

هذا يقودني إلى استنتاج خاطئ أن

a.Clicked += B.StaticHandleClicked;

من شأنه أن يؤدي إلى تسرب لأنني على الرغم من مثيل من شأنه أن تبقى الساكنة معالج.ليس هذا هو الحال (اختبار البرنامج).في حالة ثابتة معالج الحدث أو الأحداث هو العكس.إذا كنت أكتب

A.StaticClicked += b.HandleClicked;

إشارة سيتم الاحتفاظ ب.

تأكد من حالة الرجوع إلى العناصر الموجودة في الكائن هم المراجع مثل مربعات النص في عنصر تحكم النموذج.أو إذا كان لا يمكن منعها.خلق ثابت الحدث العالمي مساعد من الدرجة ثم رصد العالمي المساعد فئة الأحداث.إذا اثنين من هذه الخطوات لا يمكن أن يتم محاولة استخدام WeakReference, أنها عادة ما تكون مثالية لهذه الحالات ، ولكنها تأتي مع النفقات العامة.

لقد قرأت للتو بلوق وظيفة الخاص بك و أعتقد أن لديك قليلا من مضللة المشورة مات.إذا كان هناك الفعلية الذاكرة تسرب هنا, ثم وهذا هو الخلل في .NET Framework و ليس شيئا يمكنك بالضرورة إصلاح في التعليمات البرمجية الخاصة بك.

ما أعتقد أنك (و ملصق على بلوق الخاص بك) هي في الواقع نتحدث عنه هنا ليس في الواقع تسرب ، بل مستمرة استهلاك الذاكرة.هذا ليس نفس الشيء.أن تكون واضحة, تسربت الذاكرة هي الذاكرة التي تم حجزها عن طريق البرنامج ثم التخلي عنها (أي مؤشر ترك التعلق) ، والتي في وقت لاحق لا يمكن تحريرها.حيث تتم إدارة الذاكرة في .صافي هذا هو المستحيل نظريا.فمن الممكن أنه برنامج حجز كمية متزايدة من الذاكرة دون السماح إشارات إلى أن تخرج من نطاق (و تصبح مؤهلة لجمع القمامة);إلا أن الذاكرة لا يتم تسريب.GC العودة إلى النظام الخاص بك مرة واحدة إنهاء البرنامج.

لذلك.للإجابة على سؤالك, لا أعتقد أنك فعلا لديك مشكلة هنا.أنت بالتأكيد لم يكن لديك تسرب الذاكرة من التعليمات البرمجية, أنا لا أعتقد أنك بحاجة إلى القلق ، بقدر استهلاك الذاكرة يذهب أيضا.طالما يمكنك التأكد من أن كنت لا مرارا وتكرارا أن تعيين معالج الحدث دون أي وقت مضى دي-تعيين (أي أنك إما من أي وقت مضى فقط مجموعة مرة واحدة ، أو إزالته تماما مرة واحدة في كل مرة تعيين), الذي يبدو لك أن تفعل ، التعليمات البرمجية الخاصة بك ينبغي أن يكون على ما يرام.

يبدو أن هذه النصيحة التي الملصق على بلوق الخاص بك كنت أحاول أن أعطيك ، لكنه يستخدم هذا ينذر بالخطر العمل "تسرب" التي هي كلمة مخيفة ولكن الكثير من المبرمجين قد نسيت المعنى الحقيقي في إدارتها ؛ لا تنطبق هنا.

@السماك الرامح:

...تسد الذاكرة الخاصة بك وجعل الخاص بك تطبيق بطيئة حقا عندما لا يتم جمعها من القمامة.

وهذا واضح تمام الوضوح, و أنا لا أختلف.ومع ذلك:

أنت تسرب الذاكرة إلى كائن التي لم تعد تستخدم...لأن هناك إشارة إليها.

"يتم تخصيص الذاكرة على البرنامج و هذا البرنامج في وقت لاحق يفقد القدرة على الوصول إليه بسبب منطق البرنامج العيوب" (ويكيبيديا "تسرب الذاكرة")

إذا كان هناك النشطة مرجع إلى كائن التي يمكن البرنامج الوصول ، ثم من خلال التعريف فإنه ليس من تسرب الذاكرة.تسرب ما يعني أن الكائن لم يعد موجودا (لك أو OS/الإطار) ، ولن يتحرر عن عمر نظام التشغيل الدورة الحالية.هذا ليس هو الحال هنا.

(آسف الدلالي النازية...ربما أنا قليلا من المدرسة القديمة ، ولكن تسرب جدا معنى محدد.الناس يميلون إلى استخدام "تسرب الذاكرة" في هذه الأيام أن يعني أي شيء يستهلك 2KB من الذاكرة أكثر مما تريد...)

ولكن بالطبع إذا كنت لا تطلق معالج الحدث ، والهدف من ذلك هو تعلق لا يتحرر حتى عملية' الذاكرة المستصلحة من قبل هواة جمع القمامة عند إيقاف التشغيل.ولكن هذا السلوك هو المتوقع تماما, على عكس ما يبدو أنك تعني.إذا كنت تتوقع كائن يمكن استصلاحها ، فإنك تحتاج إلى إزالة أي شيء قد تبقى الإشارة على قيد الحياة ، بما في ذلك معالجات الأحداث.

صحيح صحيح ،

أنت على حق بالطبع..ولكن هناك جيل جديد من المبرمجين الذين يولدون في هذا العالم الذي لن لمس التعليمات البرمجية غير المدارة, وأعتقد اللغة التعاريف إعادة اختراع نفسها مرارا وتكرارا.تسرب الذاكرة في WPF في هذه الطريقة مختلفة من القول C/Cpp.

أو إلى مديري أشرت إلى أنها تسرب الذاكرة..إلى الزملاء أشرت إليها على أنها مشكلة الأداء!

في اشارة الى مات المشكلة قد تكون مشكلة الأداء التي قد تحتاج إلى معالجة.إذا كنت تستخدم فقط عدد قليل من الشاشات و جعل تلك الضوابط الشاشة ووحدانية ، قد لا تظهر هذه المشكلة على الإطلاق؛).

جيدا أن (مدير بت) أنا بالتأكيد يمكن أن نفهم ، ويتعاطفون مع.

ولكن ما تسميه مايكروسوفت ، لا أعتقد أن "الجديد" التعريف المناسب.الأمر معقد لأننا لا نعيش في 100% تمكن العالم (على الرغم من أن Microsoft يحب التظاهر الذي نقوم به ، مايكروسوفت نفسها لا نعيش في مثل هذا العالم).عندما تقول تسرب الذاكرة ، يمكن أن يعني أن البرنامج يستهلك الكثير من الذاكرة (هذا المستخدم تعريف) ، أو أن تمكنت المرجعية لن يتحرر حتى الخروج (كما هنا) أو غير مدارة المرجعية لا يتم بشكل صحيح تنظيف (حقيقة تسرب الذاكرة) ، أو أن التعليمات البرمجية غير المدارة يسمى من التعليمات البرمجية المدارة تسرب الذاكرة (آخر الحقيقي تسرب).

في هذه الحالة, من الواضح ما "تسرب الذاكرة" يعني ، على الرغم من أننا غير دقيقة.لكنه يحصل بفظاعة مملة أتحدث مع بعض الناس الذين يسمون كل الإفراط في الاستهلاك ، أو فشل في جمع ذاكرة تسرب ؛ وأنه أمر محبط عندما هؤلاء الناس هم المبرمجين الذين من المفترض أن تعرف بشكل أفضل.انها نوع من أهمية المصطلحات التقنية إلى غموض المعاني ، على ما أعتقد.التصحيح أسهل بكثير عندما تفعل ذلك.

على أي حال.لا أقصد أن يتحول هذا إلى مهواة-جنية النقاش حول اللغة.فقط أقول...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top