ما هي مزايا وعيوب وجود بت علامة معا وفصل لجمع القمامة

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

سؤال

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

هل يمكن لأي شخص أن يخبرني بالتفصيل ما هي مزايا وعيوب وجود ذاكرة منفصلة لبتات العلامات وعدم وجود ذاكرة منفصلة لبتات العلامات ?

لم أتمكن من الحصول على هذا الاختلاف من خلال مشاهدة الفيديو.

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

المحلول

بعض مزايا الصورة النقطية المنفصلة:

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

بعض مزايا بتات العلامات داخل الكائن:

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

نصائح أخرى

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

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

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

تنقسم عملية جمع البيانات المهملة Mark & ​​Sweep إلى مرحلتين:وضع العلامات والكنس.يعد وضع العلامات باستخدام بتات العلامات داخل الكائن أمرًا مباشرًا (رمز زائف):

if not obj.is_marked():
    obj.mark()
    mark_stack.append(obj)

باستخدام مصفوفة منفصلة لتخزين بتات العلامات، يتعين علينا تحويل عنوان الكائنات وحجمها إلى مؤشرات في مصفوفة البتات وتعيين البتات المقابلة على 1:

obj_bits = obj.size_in_bytes() / 16
bit_idx = (obj - heap.start_address()) / 16
if not bitarr.bit_set(bit_idx):
    bitarr.set_range(bit_idx, obj_bits)
    mark_stack.append(obj)

لذلك في مثالنا، إذا كان طول الكائن 128 بايت، فسيتم تعيين 8 بتات في مصفوفة البت.من الواضح أن استخدام بتات العلامات داخل الكائن أسهل بكثير.

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

iter = heap.start_address()
while iter < heap.end_address():
    # Scan til the next unmarked object
    while iter.is_marked():
        iter.unmark()
        iter += iter.size()
        if iter == heap.end_address():
            return
    # At an unmarked block
    start = iter
    # Scan til the next marked object
    while iter < heap.end_address() and not iter.is_marked():
        iter += iter.size()
    size = iter - start
    # Reclaim the block
    heap.reclaim(start, size)

لاحظ كيف ينتقل التكرار من كائن إلى آخر في ملف iter += iter.size() خطوط.وهذا يعني أن وقت تشغيل مرحلة المسح يتناسب مع العدد الإجمالي للكائنات الحية والكائنات المهملة.

باستخدام بتات العلامات المنفصلة، ​​يمكنك القيام بنفس الحلقة تقريبًا باستثناء أن مساحات كبيرة من الكائنات غير المرغوب فيها سيتم نقلها دون "توقف" على كل منها.

خذ بعين الاعتبار الكومة 65536 مرة أخرى.لنفترض أنه يحتوي على 4096 كائنًا كلها مهملات.من الواضح أن تكرار الأعداد الصحيحة 64 بت 64 بت في مصفوفة بتات العلامات ورؤية أنها كلها 0 هو أمر سريع جدًا.لذلك، من المحتمل أن تكون مرحلة الكنس أسرع بكثير باستخدام بتات العلامات المنفصلة.

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

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