في هذه المقالة ستجد مصدرين للمعلومات في آن واحد:
- دورة كاملة لجمع القمامة باللغة الروسية: CLRium # 6 ( ورشة العمل الحالية هنا )
- ترجمة مقال من BOTR "جهاز جمع القمامة" من تأليف Maoni Stevens.

1. CLRium # 5: دورة جامع القمامة كاملة

2. جهاز تجميع القمامة بواسطة Maoni Stephens ( @ maoni0 )
ملاحظة: لمعرفة المزيد حول جمع القمامة بشكل عام ، راجع كتيب جمع القمامة ؛ يتم توفير معلومات متخصصة حول أداة تجميع مجمعي البيانات المهملة في CLR في كتاب Pro .NET Memory Management . وترد روابط لكلا الموارد في نهاية الوثيقة.
بنية المكون
يرتبط جمع القمامة بمكونين: موزع وجامع. يكون الموزع مسؤولاً عن تخصيص الذاكرة واستدعاء المجمع إذا لزم الأمر. يجمع المجمّع البيانات المهملة أو ذاكرة الكائنات التي لم يعد يتم استخدامها بواسطة البرنامج.
هناك طرق أخرى للاتصال بالجامع ، على سبيل المثال يدويًا ، باستخدام GC.Collect. أيضًا ، قد يتلقى مؤشر ترابط finalizer إخطارًا غير متزامن بأن الذاكرة تنفد (مما سيؤدي إلى المجمع).
جهاز الموزع
يتم استدعاء الموزع بواسطة المكونات المساعدة من وقت التشغيل مع المعلومات التالية:
- الحجم اللازم للمخطط المخصص ؛
- سياق تخصيص الذاكرة لمؤشر التنفيذ ؛
- إشارات تشير ، على سبيل المثال ، إلى ما إذا كان الكائن قابل للتحديد.
لا يوفر أداة تجميع مجمعي البيانات المهملة طرق معالجة خاصة لأنواع مختلفة من الكائنات. يتلقى معلومات حول حجم الكائن من وقت التشغيل.
بناءً على الحجم ، يقسم المجمع الكائنات إلى فئتين: صغير (<85000 بايت) وكبير (> = 85000 بايت). بشكل عام ، يمكن أن يحدث تجميع الكائنات الصغيرة والكبيرة بالطريقة نفسها. ومع ذلك ، يفصل المجمّع بينها حسب الحجم ، لأن ضغط الكائنات الكبيرة يتطلب الكثير من الموارد.
يخصص أداة تجميع مجمعي البيانات المهملة الذاكرة إلى المُخصص استنادًا إلى سياقات التخصيص. يتم تحديد حجم سياق التخصيص بواسطة كتل الذاكرة المخصصة.
سياقات التحديد هي مساحات صغيرة من مقطع كومة معين ، كل منها مخصص لتدفق تنفيذ محدد. على جهاز به معالج واحد (بمعنى معالج منطقي واحد) ، يتم استخدام سياق تخصيص ذاكرة واحدة لكائنات الجيل 0.
كتلة الذاكرة المخصصة - مقدار الذاكرة التي خصصها المُخصص في كل مرة يحتاج فيها إلى مزيد من الذاكرة لوضع كائن داخل المنطقة. حجم الكتلة هو عادة 8 كيلو بايت ، ومتوسط حجم الكائنات المدارة هو 35 بايت. لذلك ، في كتلة واحدة يمكنك وضع العديد من الكائنات.
الكائنات الكبيرة لا تستخدم السياقات والكتل. قد يكون كائن كبير واحد أكبر من هذه القطع الصغيرة من الذاكرة. بالإضافة إلى ذلك ، تظهر فوائد استخدام هذه المناطق (الموضحة أدناه) فقط عند العمل مع الكائنات الصغيرة. يتم تخصيص مساحة للكائنات الكبيرة مباشرة في مقطع الكومة.
الموزع مصمم بحيث:
استدعاء أداة تجميع مجمعي البيانات المهملة عند الضرورة: يقوم الموزع باستدعاء أداة التجميع عندما تتجاوز كمية الذاكرة المخصصة للكائنات القيمة الحدية (التي حددها المجمع) ، أو إذا لم يعد بإمكان الموزع تخصيص ذاكرة في هذا الجزء. سيتم وصف العتبات وشرائح التحكم بالتفصيل لاحقًا.
حفظ موقع الكائنات: يتم تخزين الكائنات الموجودة في جزء واحد من الكومة في عناوين افتراضية قريبة من بعضها البعض.
استخدم ذاكرة التخزين المؤقت بكفاءة: يخصص التخصيص الذاكرة في كتل ، وليس لكل كائن. يعمل على تخزين الكثير من الذاكرة لإعداد ذاكرة التخزين المؤقت للمعالج ، حيث سيتم وضع بعض الكائنات مباشرة فيه. كتلة الذاكرة المخصصة عادةً 8 كيلو بايت.
تحديد المساحة المخصصة لمؤشر التنفيذ بشكل فعال: يضمن القرب من السياقات وكتل الذاكرة المخصصة لمؤشر الترابط أن يقوم مؤشر ترابط واحد فقط بكتابة البيانات إلى المساحة المخصصة. نتيجة لذلك ، ليست هناك حاجة للحد من تخصيص الذاكرة حتى تنتهي المساحة الموجودة في سياق التخصيص الحالي.
تأكد من تكامل الذاكرة: يقوم جامع البيانات المهملة دائمًا بإخراج الذاكرة للكائنات المخصصة حديثًا حتى لا تشير ارتباطاتها إلى أقسام عشوائية من الذاكرة.
تأكد من استمرارية الكومة: يقوم المُخصص بإنشاء كائن حر من الذاكرة المتبقية في كل كتلة مخصصة. على سبيل المثال ، إذا بقيت 30 بايت في الكتلة ، وكانت هناك حاجة إلى 40 بايت لاستيعاب الكائن التالي ، فسيقوم المُخصص بتحويل هذه البايتات 30 إلى كائن حر ويطلب كتلة ذاكرة جديدة.
API
Object* GCHeap::Alloc(size_t size, DWORD); Object* GCHeap::Alloc(alloc_context* acontext, size_t size, DWORD);
باستخدام هذه الوظائف ، يمكنك تخصيص ذاكرة لكلا الكائنات الصغيرة والكبيرة. هناك وظيفة لتخصيص مساحة مباشرة على كومة الكائنات الكبيرة (LOH):
Object* GCHeap::AllocLHeap(size_t size, DWORD);
جهاز جامع
مهام جامع القمامة
تم تصميم GC لإدارة الذاكرة بكفاءة. يمكن للمطورين الذين يكتبون التعليمات البرمجية المدارة استخدامها دون بذل جهد كبير. الحكم الرشيد يعني:
- يجب أن يحدث تجميع البيانات المهملة بشكلٍ كافٍ حتى لا تتناثر الكومة المدارة بعدد كبير (حسب النسبة أو بالكمية المطلقة) من الكائنات غير المستخدمة (البيانات المهملة) التي تم تخصيص الذاكرة لها ؛
- يجب أن يحدث جمع البيانات المهملة في حالات نادرة قدر الإمكان حتى لا تضيع وقت معالج مفيدًا ، على الرغم من أن التجميع المتكرر سيسمح باستخدام ذاكرة أقل ؛
- يجب أن يكون تجميع البيانات المهملة مثمرًا ، لأنه إذا تم تجميع جزء صغير من الذاكرة فقط نتيجة للتجميع ، فكان كل من التجميع ووقت المعالج المستغرق دون جدوى ؛
- يجب أن يكون تجميع البيانات المهملة سريعًا ، نظرًا لأن العديد من أعباء العمل تتطلب وقتًا قصيرًا للتأخير ؛
- لا يحتاج المطورون الذين يكتبون التعليمات البرمجية المدارة إلى معرفة الكثير حول تجميع البيانات المهملة من أجل تحقيق الاستخدام الفعال للذاكرة (مقارنةً بحجم العمل الخاص بهم) ؛
- يجب أن يتكيف جامع البيانات المهملة مع الطبيعة المختلفة لاستخدام الذاكرة.
الوصف المنطقي للكومة المدارة
يجمع جامع البيانات المهملة CLR الكائنات التي يتم فصلها منطقياً بواسطة إنشاء. بعد تجميع الكائنات في الجيل N ، يتم تمييز الكائنات المتبقية على أنها تنتمي إلى الجيل N + 1 . تسمى هذه العملية الترويج للكائنات عبر الأجيال. هناك استثناءات في هذه العملية عندما يكون من الضروري نقل كائن إلى جيل أقل أو عدم تقدمه على الإطلاق.
في حالة الأشياء الصغيرة ، يتم تقسيم الكومة إلى ثلاثة أجيال: gen0 و gen1 و gen2. للكائنات الكبيرة ، هناك جيل واحد فقط - gen3. تسمى Gen0 و gen1 بالأجيال سريعة الزوال (الكائنات تعيش فيها لفترة قصيرة).
بالنسبة لمجموعة من الأشياء الصغيرة ، فإن رقم الجيل يعني عمرهم. على سبيل المثال ، gen0 هو أصغر جيل. هذا لا يعني أن جميع الكائنات في gen0 أصغر من الكائنات في gen1 أو gen2. هناك استثناءات الموضحة أدناه. يعني تجميع الجيل تجميع الكائنات في هذا الجيل ، وكذلك في جميع الأجيال الشابة.
من الناحية النظرية ، يمكن أن يحدث تجميع الأشياء الكبيرة والصغيرة بنفس الطريقة. ومع ذلك ، نظرًا لأن ضغط الكائنات الكبيرة يتطلب الكثير من الموارد ، يحدث التجميع بطريقة مختلفة. الكائنات الكبيرة موجودة فقط في gen2 ويتم تجميعها فقط أثناء تجميع البيانات المهملة في هذا الجيل لأسباب تتعلق بالأداء. يمكن أن يكون كل من gen2 و gen3 كبيرًا ، ويجب ألا يكون بناء كائن في الأجيال سريعة الزوال (gen0 و gen1) كثيف الاستخدام للموارد.
يتم وضع الكائنات في أصغر جيل. بالنسبة للكائنات الصغيرة ، هذا هو gen0 ، وبالنسبة للكائنات الكبيرة ، gen3.
الوصف المادي للكومة المدارة
يتكون الكومة المدارة من مجموعة من القطع. القطعة عبارة عن كتلة مستمرة من الذاكرة يقوم نظام التشغيل بتمريرها إلى أداة تجميع مجمعي البيانات المهملة. تنقسم مقاطع الكومة إلى أقسام صغيرة وكبيرة لاستيعاب الكائنات الصغيرة والكبيرة. ترتبط شرائح كل كومة معًا. يتم حجز مقطع واحد على الأقل لكائن صغير وجزء واحد كبير عند تحميل CLR.
في كل كومة من الكائنات الصغيرة يوجد مقطع واحد سريع الزوال ، حيث توجد الأجيال gen0 و gen1. قد يحتوي هذا الجزء أو لا يحتوي على كائنات توليد gen2. بالإضافة إلى الأجزاء المؤقتة ، قد يوجد جزء أو أكثر من الشرائح الإضافية ، والتي ستكون شرائح gen2 ، لأنها تحتوي على كائنات من الجيل 2.
تتكون كومة الكائنات الكبيرة من مقطع واحد أو أكثر.
يتم تعبئة مقطع كومة الذاكرة المؤقتة من عناوين أقل إلى أعلى. هذا يعني أن الكائنات الموجودة في العناوين السفلية للجزء أقدم من الكائنات الموجودة في الجزء العلوي. هناك أيضًا استثناءات موضحة أدناه.
يتم تخصيص مقاطع الكومة حسب الحاجة. إذا كانت لا تحتوي على كائنات مستعملة ، فسيتم حذف الأجزاء. ومع ذلك ، دائماً قطعة الأولي على الكومة موجود. يتم تخصيص قطعة واحدة في وقت واحد لكل كومة. في حالة الأشياء الصغيرة ، يحدث هذا أثناء جمع البيانات المهملة ، والأشياء الكبيرة ، أثناء تخصيص الذاكرة لها. يعمل هذا المخطط على زيادة الإنتاجية ، نظرًا لأن الكائنات الكبيرة يتم تجميعها فقط في الجيل 2 (الأمر الذي يتطلب الكثير من الموارد).
يتم ضم شرائح الكومة معًا في التحديدات. الجزء الأخير في السلسلة دائمًا ما يكون سريع الزوال. يمكن إعادة استخدام الأجزاء التي يتم جمع كل الكائنات فيها ، على سبيل المثال ، سريعة الزوال. ينطبق إعادة استخدام الشريحة فقط على أكوام الكائنات الصغيرة. لاستيعاب الأشياء الكبيرة في كل مرة يتم النظر في مجموعة كاملة من الأشياء الكبيرة. توضع الأشياء الصغيرة فقط في مقاطع سريعة الزوال.
قيمة عتبة الذاكرة المخصصة
هذا مفهوم منطقي يتعلق بحجم كل جيل. إذا تم تجاوزه ، يبدأ الجيل في جمع البيانات المهملة.
يتم تعيين قيمة العتبة لجيل معين اعتمادًا على عدد الكائنات الباقية في هذا الجيل. إذا كان هذا المبلغ مرتفعًا ، فستصبح قيمة العتبة أعلى. من المتوقع أن تكون نسبة الكائنات المستخدمة وغير المستخدمة أفضل خلال جلسة جمع البيانات المهملة من الجيل التالي.
اختيار الجيل لجمع القمامة
عند التنشيط ، يجب على المجمع تحديد الجيل الذي سيتم إنشاؤه. بالإضافة إلى قيمة العتبة ، هناك عوامل أخرى تؤثر على هذا الاختيار:
- تجزئة جيل - إذا كان جيل مجزأ للغاية ، فمن المرجح أن يكون جمع القمامة فيه مثمرًا ؛
- إذا كانت ذاكرة الجهاز مشغولة للغاية ، فيمكن للمجمع إجراء تنظيف أعمق ، إذا كان هذا التنظيف أكثر احتمالًا لإفساح مساحة وتجنب تبادل الصفحات غير الضرورية (الذاكرة في الجهاز بالكامل) ؛
- في حالة نفاد مساحة سريعة الزوال ، يمكن للمجمع إجراء تنظيف أعمق في هذا الجزء (جمع المزيد من الكائنات من الجيل 1) لتجنب تخصيص مقطع كومة جديد.
عملية جمع القمامة
بمناسبة المرحلة
خلال هذه المرحلة ، يجب أن تجد CLR جميع الكائنات الحية.
تتمثل ميزة المجمع في دعم الأجيال في قدرته على تنظيف القمامة فقط في جزء من الكومة ، بدلاً من مراقبة كل الكائنات باستمرار. جمع القمامة في الأجيال سريعة الزوال ، يجب على المجمع الحصول على معلومات من بيئة وقت التشغيل حول الكائنات في هذه الأجيال لا تزال تستخدم من قبل البرنامج. بالإضافة إلى ذلك ، يمكن للكائنات في الأجيال الأقدم استخدام الكائنات في الأجيال الشابة ، بالإشارة إليها.
لتمييز الكائنات القديمة التي تشير إلى كائنات جديدة ، يستخدم أداة تجميع مجمعي البيانات المهملة بتات خاصة. يتم تعيين البتات بواسطة آلية مترجم JIT أثناء عمليات التعيين. إذا كان الكائن ينتمي إلى جيل سريع الزوال ، فسوف يقوم برنامج التحويل البرمجي JIT بتعيين البايت الذي يحتوي على البتة التي تشير إلى الموضع الأولي. عند جمع البيانات المهملة في الأجيال سريعة الزوال ، يمكن للمجمع استخدام هذه البتات لكومة الذاكرة المؤقتة المتبقية وعرض الكائنات التي تتوافق معها هذه البتات فقط.
مرحلة التخطيط
في هذه المرحلة ، يتم تصميم الضغط لتحديد فعاليته. إذا كانت النتيجة مثمرة ، يبدأ الجامع في الضغط الفعلي. خلاف ذلك ، يفعل التنظيف فقط.
الانتقال المرحلة
إذا كان المجمع يقوم بالضغط ، فسيؤدي ذلك إلى تحريك الكائنات. في هذه الحالة ، يجب عليك تحديث الارتباطات لهذه الكائنات. أثناء مرحلة النقل ، يجب أن يجد المجمع جميع الروابط التي تشير إلى الكائنات في الأجيال التي تحدث فيها مجموعة البيانات المهملة. على النقيض من ذلك ، أثناء مرحلة وضع العلامات ، يقوم المجمّع بتمييز الكائنات الحية فقط ، لذلك لا يحتاج إلى مراعاة الروابط الضعيفة.
مرحلة الضغط
هذه المرحلة بسيطة للغاية ، نظرًا لأن المجمع قد حدد بالفعل عناوين جديدة لنقل الكائنات أثناء مرحلة التخطيط. عند الضغط ، سيتم نسخ الكائنات إلى هذه العناوين.
مرحلة التنظيف
خلال هذه المرحلة ، يبحث المجمع عن مساحة غير مستخدمة بين الكائنات الحية. بدلا من هذا الفضاء ، وقال انه يخلق كائنات مجانية. الكائنات غير المستخدمة في المنطقة المجاورة تصبح كائنًا واحدًا مجانيًا. يتم وضع جميع الكائنات المجانية في قائمة الكائنات المجانية .
تدفق الرمز
حيث:
- WKS GC: مجموعة البيانات المهملة في وضع محطة العمل
- SVR GC: مجموعة Garbage وضع الخادم
السلوك الوظيفي
WKS GC دون جمع القمامة المتوازي
- استخدم مؤشر ترابط المستخدم كافة الذاكرة المخصصة لذلك ويستدعي أداة تجميع مجمعي البيانات المهملة.
- يدعو جامع
SuspendEE
إلى تعليق كافة مؤشرات الترابط المدارة. - يختار المجمع جيلًا للتنظيف.
- تبدأ علامات الكائنات.
- يذهب المجمّع إلى مرحلة التخطيط ويحدد الحاجة إلى الضغط.
- إذا لزم الأمر ، يقوم المجمع بنقل الكائنات وتنفيذ الضغط. في حالة أخرى ، يفعل التنظيف فقط.
- يستدعي المجمع
RestartEE
لإعادة تشغيل مؤشرات الترابط المدارة. - تستمر مؤشرات ترابط المستخدم في العمل.
WKS GC مع جمع القمامة المتوازي
توضح هذه الخوارزمية مجموعة البيانات المهملة الخلفية.
- استخدم مؤشر ترابط المستخدم كافة الذاكرة المخصصة لذلك ويستدعي أداة تجميع مجمعي البيانات المهملة.
- يدعو جامع
SuspendEE
إلى تعليق كافة مؤشرات الترابط المدارة. - يحدد المجمع ما إذا كان سيتم تشغيل مجموعة البيانات المهملة الخلفية.
- إذا كان الأمر كذلك ، يتم تنشيط مؤشر ترابط مجموعة البيانات المهملة في الخلفية. يستدعي هذا مؤشر الترابط
RestartEE
لاستئناف مؤشرات الترابط المدارة. - يستمر تخصيص الذاكرة للعمليات المدارة في نفس الوقت مثل تجميع البيانات المهملة في الخلفية.
- يمكن أن يستخدم مؤشر ترابط المستخدم جميع الذاكرة المخصصة له وبدء مجموعة البيانات المهملة سريعة الزوال (المعروفة أيضًا باسم مجموعة البيانات المهملة ذات الأولوية العليا). يتم تشغيله بنفس طريقة تشغيل محطة العمل دون تجميع البيانات المهملة المتوازية.
SuspendEE
مجموعة البيانات المهملة الخلفية يستدعي SuspendEE
مرة أخرى لإكمال وضع العلامات ، ثم يستدعي RestartEE
لبدء تنظيف متوازي مع تشغيل مؤشرات RestartEE
المستخدم.- اكتمال جمع القمامة الخلفية.
SVR GC دون جمع القمامة المتوازي
- استخدم مؤشر ترابط المستخدم كافة الذاكرة المخصصة لذلك ويستدعي أداة تجميع مجمعي البيانات المهملة.
- يتم تنشيط مؤشرات الترابط مجموعة البيانات المهملة وضع الخادم وتتسبب
SuspendEE
لإيقاف تنفيذ مؤشرات الترابط المدارة. - تقوم تدفقات تجميع البيانات المهملة في وضع الخادم بنفس العمليات كما في وضع محطة العمل دون تجميع البيانات المهملة المتوازية.
- استدعاء مؤشرات الترابط مجموعة البيانات المهملة وضع الخادم
RestartEE
لبدء مؤشرات الترابط المدارة. - تواصل مواضيع المستخدم العمل.
SVR GC مع جمع القمامة المتوازي
الخوارزمية هي نفسها كما في حالة تجميع البيانات المهملة المتوازية في وضع محطة العمل ، يتم تنفيذ مجموعة غير phonon فقط في مؤشرات ترابط الخادم.
العمارة الفيزيائية
سيساعدك هذا القسم على فهم تدفق الكود.
عندما ينفد مؤشر ترابط المستخدم من الذاكرة ، يمكنه الحصول على مساحة خالية باستخدام دالة try_allocate_more_space
.
try_allocate_more_space
الدالة GarbageCollectGeneration
عندما تحتاج إلى بدء أداة تجميع مجمعي البيانات المهملة.
إذا كانت مجموعة البيانات المهملة في وضع محطة العمل غير متوازية ، يتم تنفيذ GarbageCollectGeneration
في مؤشر ترابط المستخدم الذي دعا إليه جامع البيانات المهملة. دفق التعليمات البرمجية كما يلي:
GarbageCollectGeneration() { SuspendEE(); garbage_collect(); RestartEE(); } garbage_collect() { generation_to_condemn(); gc1(); } gc1() { mark_phase(); plan_phase(); } plan_phase() { // , // if (compact) { relocate_phase(); compact_phase(); } else make_free_lists(); }
إذا تم تنفيذ مجموعة البيانات المهملة المتوازية في وضع محطة العمل (بشكل افتراضي) ، فإن تدفق التعليمات البرمجية لجمع البيانات المهملة في الخلفية يبدو كما يلي:
GarbageCollectGeneration() { SuspendEE(); garbage_collect(); RestartEE(); } garbage_collect() { generation_to_condemn(); // // do_background_gc(); } do_background_gc() { init_background_gc(); start_c_gc (); // . wait_to_proceed(); } bgc_thread_function() { while (1) { // // gc1(); } } gc1() { background_mark_phase(); background_sweep(); }
روابط الموارد