كيفية تجنب جمع البيانات المهملة في الوقت الحقيقي لتطبيق .NET؟

StackOverflow https://stackoverflow.com/questions/85283

سؤال

أنا أكتب تطبيقًا ماليًا C# يتلقى الرسائل من الشبكة ويترجمها إلى كائن مختلف وفقًا لنوع الرسالة ويطبق أخيرًا منطق أعمال التطبيق عليها.

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

هل هناك طريقة أفضل للقيام بذلك في C#، هل يجب أن أستخدم مجموعة من الكائنات لإعادة استخدام نفس مجموعة المثيلات دائمًا أم أن هناك استراتيجية أفضل.

الهدف هو تجنب تجميع البيانات المهملة لاستخدام أي وحدة معالجة مركزية أثناء عملية زمنية حرجة.

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

المحلول

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

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

نصائح أخرى

انظر هنا: http://msdn.microsoft.com/en-us/library/bb384202.aspx

يمكنك أن تخبر جامع القمامة أنك تفعل شيئًا مهمًا في الوقت الحالي، وسيحاول أن يكون لطيفًا معك.

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

عندما يصبح الحمل على حمام السباحة الخاص بك منخفضًا بدرجة كافية، يمكنك تقليص حجم حمام السباحة وسيؤدي ذلك في النهاية إلى تجميع البيانات المهملة - دع CLR يقلق بشأن ذلك.

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

في الحالة التي ذكرتها، يبدو الأمر كما لو أنك تقوم بإنشاء عدد من الكائنات قصيرة العمر - سيؤدي ذلك إلى مجموعات Gen0.تحدث هذه الأمور في كثير من الأحيان نسبيًا على أي حال، وهي الأكثر كفاءة.يمكنك تجنبها من خلال وجود مجموعة من الكائنات القابلة لإعادة الاستخدام، إذا كنت تفضل ذلك، ولكن من الأفضل التأكد مما إذا كانت GC تمثل مشكلة في الأداء قبل اتخاذ مثل هذا الإجراء - فإن ملف تعريف CLR هو الأداة للقيام بذلك.

تجدر الإشارة إلى أن أداة تجميع البيانات المهملة تختلف عن أطر عمل .NET المختلفة - فهي في إطار العمل المضغوط (الذي يعمل على Xbox 360 وعلى منصات الأجهزة المحمولة) عبارة عن GC غير جيلي، وعلى هذا النحو يجب أن تكون أكثر حذرًا بشأن ما القمامة التي ينشئها برنامجك.

يعد فرض GC.Collect() فكرة سيئة بشكل عام، اترك GC للقيام بما هو أفضل.يبدو أن الحل الأفضل هو استخدام مجموعة من الكائنات التي يمكنك تنميتها إذا لزم الأمر - لقد استخدمت هذا النمط بنجاح.

بهذه الطريقة لا تتجنب جمع البيانات المهملة فحسب، بل تتجنب أيضًا تكلفة التخصيص المنتظمة.

أخيرًا، هل أنت متأكد من أن GC يسبب لك مشكلة؟ربما ينبغي عليك قياس ذلك وإثباته قبل تنفيذ أي حلول لتوفير الأداء - فقد تسبب لنفسك عملاً غير ضروري!

"الهدف هو تجنب تجميع البيانات المهملة لاستخدام أي وحدة معالجة مركزية أثناء عملية زمنية حرجة"

س: إذا كان الوقت حرجًا، فهذا يعني أنك تستمع إلى بعض الأجهزة الباطنية، ولا يمكنك تفويت المقاطعة؟

أ: إذا كان الأمر كذلك، فإن C# ليست اللغة التي يجب استخدامها، فأنت تريد Assembler أو C أو C++ لذلك.

س: إذا كان الوقت حرجًا، فهذا يعني وجود الكثير من الرسائل في الأنبوب، ولا تريد السماح لأداة تجميع البيانات المهملة بإبطاء الأمور؟

أ: إذا كان الأمر كذلك فأنت تقلق دون داع.من خلال أصوات الأشياء، فإن الكائنات الخاصة بك قصيرة العمر جدًا، وهذا يعني أن جامع القمامة سيقوم بإعادة تدويرها بكفاءة عالية، دون أي تأخير واضح في الأداء.

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

احصل على فهم جيد وشعور جيد بكيفية تصرف جامع البيانات المهملة، وسوف تفهم سبب عدم التوصية بما تفكر فيه هنا.إلا إذا كنت تحب CLR حقًا لقضاء بعض الوقت في إعادة ترتيب الكائنات في الذاكرة كثيراً.

ما مدى كثافة التطبيق؟لقد كتبت تطبيقًا يلتقط 3 بطاقات صوت (Managed DirectX، 44.1 كيلو هرتز، استريو، 16 بت)، في كتل بحجم 8 كيلو بايت، ويرسل 2 من التدفقات الثلاثة إلى كمبيوتر آخر عبر TCP/IP.تعرض واجهة المستخدم مقياسًا لمستوى الصوت وعنوانًا/فنانًا للتمرير (سلسًا) لكل قناة من القنوات الثلاث.يعمل هذا على أجهزة الكمبيوتر التي تعمل بنظام XP و1.8 جيجا هرتز و512 ميجابايت وما إلى ذلك.يستخدم التطبيق حوالي 5% من وحدة المعالجة المركزية.

لقد ابتعدت عن الاتصال بطرق GC يدويًا.لكن كان عليّ ضبط بعض الأشياء التي كانت مضيعة للوقت.لقد استخدمت ملف تعريف Ant الخاص بـ RedGate للتركيز على الأجزاء المهدرة.أداة رهيبة!

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

إذا كان الوقت حرجًا تمامًا، فيجب عليك استخدام منصة حتمية مثل C/C++.حتى استدعاء GC.Collect() سيؤدي إلى إنشاء دورات وحدة المعالجة المركزية.

يبدأ سؤالك باقتراح أنك تريد حفظ الذاكرة مع التخلص من الأشياء.هذا هو التحسين الحاسم للمساحة.عليك أن تقرر ما تريده حقًا لأن GC أفضل في تحسين هذا الموقف من الإنسان.

من الصوت، يبدو أنك تتحدث عن الإنهاء الحتمي (المدمرات في C++)، وهو غير موجود في C#.أقرب شيء ستجده في C# هو النمط القابل للتصرف.في الأساس تقوم بتنفيذ يمكن التخلص منه واجهه المستخدم.

النمط الأساسي هو هذا:

public class MyClass: IDisposable
{
    private bool _disposed;

    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected virtual void Dispose( bool disposing )
    {
        if( _disposed )    
            return;

        if( disposing )
        {
            // Dispose managed resources here
        }

        _disposed = true;
    }
}

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

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

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

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

** "تجميع الكائنات هو نمط يمكن استخدامه ويسمح بإعادة استخدام الكائنات بدلاً من تخصيصها وإلغاء تخصيصها، مما يساعد على منع تجزئة الكومة بالإضافة إلى عمليات ضغط GC المكلفة."

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pooling-and.aspx

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

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