[C#] لماذا لا العد المرجعي + جمع القمامة في C #؟


Answers

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

و غ تشير إلى العد - ومع ذلك فإنه يفعل ذلك بطريقة مختلفة من خلال العثور على الكائنات التي يمكن الوصول إليها ( Ref Count > 0 ) في كل مرة تقوم به مجموعة ... انها مجرد لا تفعل ذلك بطريقة مضادة صحيح. . يتم جمع الكائنات التي لا يمكن الوصول إليها ( Ref Count = 0 ). وبهذه الطريقة وقت التشغيل لم يكن لديك للقيام التدبير المنزلي / تحديث الجداول في كل مرة يتم تعيين كائن أو الإفراج ... يجب أن يكون أسرع.

الفرق الرئيسي الوحيد بين C ++ (حتمية) و C # (غير محدد) هو عندما يتم تنظيف الكائن. لا يمكنك التنبؤ بالوقت المحدد الذي سيتم فيه جمع عنصر في C #.

أوتبتينث بلوغ: أوصي بقراءة فصل ستاندرد جيفري ريشتر في القاهرة الكبرى في كلر عبر C # في حال كنت مهتما حقا في كيفية عمل غ.

Question

جئت من خلفية C ++ وكنت أعمل مع C # لمدة عام تقريبا. مثل العديد من الآخرين أنا فلوموكسد لماذا لماذا إدارة الموارد الحتمية ليست مدمجة في اللغة. بدلا من المدمرات الحتمية لدينا نمط التخلص. يبدأ الناس في التساؤل عما إذا كان نشر السرطان القابل للإصابة من خلال قانونهم يستحق هذا الجهد.

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

ماذا لو تم تعديل C # بحيث:

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

  • لماذا هذه فكرة سيئة؟
  • هل هذا هزيمة الغرض من جامع القمامة؟
  • هل سيكون من الممكن تنفيذ مثل هذا الشيء؟

تحرير: من التعليقات حتى الآن، وهذا هو فكرة سيئة لأنه

  1. غ أسرع دون عد مرجع
  2. مشكلة التعامل مع دورات في الرسم البياني الكائن

أعتقد أن الرقم الأول صحيح، ولكن الرقم الثاني سهل التعامل مع استخدام المراجع الضعيفة.

لذا فإن سرعة التحسين تفوق سلبيات أن:

  1. قد لا تحرير مورد غير الذاكرة في الوقت المناسب
  2. قد يحرر المورد غير الذاكرة قريبا جدا

إذا كانت آلية تنظيف الموارد الخاصة بك هي حتمية ومدمجة في اللغة يمكنك القضاء على تلك الاحتمالات.




هناك فرق بين C ++ نمط العد إشارة مؤشر الذكية والمرجعية جمع جمع القمامة. لقد تحدثت أيضا عن الاختلافات في مدونتي ولكن هنا ملخص سريع:

C ++ نمط مرجع العد:

  • تكلفة غير محدودة على النقصان: إذا كان جذر بنية بيانات كبيرة ينخفض ​​إلى الصفر، هناك تكلفة غير محدودة لتحرير جميع البيانات.

  • دليل دورة جمع: لمنع هياكل البيانات الدورية من تسرب الذاكرة مبرمج يجب كسر يدويا أي الهياكل المحتملة عن طريق استبدال جزء من دورة مع مؤشر الذكية ضعيفة. وهذا مصدر آخر للعيوب المحتملة.

مرجع عد جمع القمامة

  • مؤجلة أرسي: يتم تجاهل التغييرات على عدد مرجع كائن ل كومة وتسجيل المراجع. بدلا من ذلك عندما يتم تشغيل غ يتم الاحتفاظ هذه الكائنات عن طريق جمع مجموعة الجذر. يمكن تأجيل التغييرات على العدد المرجعي ومعالجتها على دفعات. وهذا يؤدي إلى زيادة الإنتاجية .

  • التجميد: باستخدام حاجز الكتابة فمن الممكن لتجميع التغييرات على العد المرجعي. وهذا يجعل من الممكن تجاهل معظم التغييرات على عدد مرجع الكائنات تحسين أداء أرسي لمراجع متحولة في كثير من الأحيان.

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

في الأساس فمن الممكن لتنفيذ عالية الأداء أرسي مقرها جمع القمامة لوقت التشغيل مثل جفم جافا و. نيت كلر وقت التشغيل.

أعتقد أن جامعي البحث عن المفقودين يستخدمون جزئيا لأسباب تاريخية: فالعديد من التحسينات الأخيرة في عد المراجع جاءت بعد إطلاق سراح جفم و .net وقت التشغيل. كما يستغرق العمل البحثي وقتا للتحول إلى مشاريع إنتاجية.

التخلص من الموارد الحاسم

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

Skrymsli ، هذا هو الغرض من الكلمة الرئيسية " استخدام ". على سبيل المثال:

public abstract class BaseCriticalResource : IDiposable {
    ~ BaseCriticalResource () {
        Dispose(false);
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this); // No need to call finalizer now
    }

    protected virtual void Dispose(bool disposing) { }
}

ثم لإضافة فئة مع مورد حاسم:

public class ComFileCritical : BaseCriticalResource {

    private IntPtr nativeResource;

    protected override Dispose(bool disposing) {
        // free native resources if there are any.
        if (nativeResource != IntPtr.Zero) {
            ComCallToFreeUnmangedPointer(nativeResource);
            nativeResource = IntPtr.Zero;
        }
    }
}

ثم لاستخدامها بسيطة مثل:

using (ComFileCritical fileResource = new ComFileCritical()) {
    // Some actions on fileResource
}

// fileResource's critical resources freed at this point

انظر أيضا تنفيذ إديسبوسابل بشكل صحيح .




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

نقطة كاملة من إديسبوسابل هو أن غ قيد التشغيل في بعض الوقت غير حتمية وكنت تنفيذ إديسبوسابل لأنك تحمل مورد قيمة ويريد تحريره في وقت حتمي.

لذا فإن اقتراحكم لن يغير شيئا من حيث قابلية التصرف.

تصحيح:

آسف. لم يقرأ اقتراحك بشكل صحيح. :-(

ويكيبيديا لديه تفسير بسيط من أوجه القصور في المراجع عد غ




إدارة الموارد غير الذاكرة حاسمة هي جزء من اللغة، ومع ذلك فإنه لم يتم مع المدمرات.

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

في لغات أخرى (C #، جافا، بيثون، روبي، إرلانغ، ...) يمكنك استخدام محاولة أخيرا (أو محاولة-أخيرا) بدلا من التأكد من أن رمز التنظيف سوف تعمل دائما.

// Initialize some resource.
try {
    // Use the resource.
}
finally {
    // Clean-up.
    // This code will always run, whether there was an exception or not.
}

إيك #، يمكنك أيضا استخدام بناء باستخدام :

using (Foo foo = new Foo()) {
    // Do something with foo.
}
// foo.Dispose() will be called afterwards, even if there
// was an exception.

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




أنا أعرف شيئا عن جمع القمامة. هنا ملخص قصير لأن شرحا كاملا يتجاوز حدود هذا السؤال.

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

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