سأقول على الفور: لا أنتظر أبدًا إجابة مفصلة عن هذا السؤال حول الضمان الاجتماعي. هذا غبي وفي حالتي الأنانية. ومع ذلك ، في رأيي ، بالإضافة إلى الاهتمام العام بالمنصة ، من المفيد جدًا معرفة كيف تعمل ، لأنه هذا يزيل عددا من القضايا. على سبيل المثال ، يستبعد هذا الخيار عندما يعتقد المطور أن Dispose
يسمى تلقائيًا ولا تحتاج إلى تسميته بنفسك. أو إذا كان المطور أكثر خبرة ، فهو يساعده تلقائيًا ، على مستوى ذاكرة العضلات ، فيكتب رمزًا يؤدي إلى أقل عدد من المشكلات.
والسؤال الآخر الذي لا أحبه شخصيًا هو كيف يتم شرح عمله. لذلك ، أقترح طريقة بديلة موصوفة في كتابي ، .NET Platform Architecture .
إذا أردنا أن نفهم تمامًا سبب اختيار خوارزميات إدارة الذاكرة هاتين: Sweep and Compact ، فسوف يتعين علينا النظر في العشرات من خوارزميات إدارة الذاكرة الموجودة في العالم: بدءًا من القواميس العادية وتنتهي بهياكل معقدة للغاية خالية من قفل. بدلاً من ذلك ، مع ترك أفكارنا حول ما هو مفيد ، فإننا ببساطة نبرر الخيار ونفهم بالتالي سبب اتخاذ هذا الاختيار. لم نعد نلقي نظرة على كتيب إطلاق الداعم: لدينا في أيدينا مجموعة كاملة من الوثائق.
يكون النزاع مفيدًا للطرفين: إذا لم يكن واضحًا ، فسوف أقوم بتصحيح النقاط غير الواضحة في الكتاب ، والتي جزء صغير منها هو النص المحدد.

لقد اخترت تنسيق الاستدلال بحيث تشعر أنك مثل مهندسي النظام الأساسي وتوصلت إلى نفس الاستنتاجات التي توصل إليها المهندسون المعماريون الحقيقيون في مقر Microsoft في ريدموند.
استنادًا إلى تصنيف الكائنات المخصصة استنادًا إلى حجمها ، يمكنك تقسيم المساحة المخصصة لتخصيص الذاكرة إلى قسمين كبيرين: مكان به كائنات بحجم أقل من عتبة معينة وموقع به حجم أعلى من هذه العتبة ومعرفة الفرق الذي يمكن تحقيقه في إدارة هذه المجموعات (استنادًا إلى حجمها) وما يأتي منه.
إذا أخذنا في الاعتبار إدارة الكائنات " الصغيرة " التقليدية ، يمكننا أن نرى أنه إذا التزمنا بفكرة تخزين المعلومات حول كل كائن ، فسيكون مكلفًا للغاية بالنسبة لنا الحفاظ على هياكل بيانات إدارة الذاكرة التي تخزن الروابط إلى كل كائن من هذا القبيل. في النهاية ، قد يتضح أنه من أجل تخزين المعلومات حول كائن واحد ، ستحتاج إلى قدر الذاكرة التي يحتاجها الكائن نفسه. بدلاً من ذلك ، يجب أن تأخذ في الاعتبار: إذا رقصنا خلال مجموعة البيانات المهملة من الجذور ، ونذهب في عمق الرسم البياني عبر الحقول الصادرة من الكائن ، ونحتاج إلى ممر خطي على طول الكومة فقط لتحديد كائنات البيانات المهملة ، هل من الضروري بالنسبة لنا تخزين معلومات حول كل كائن في خوارزميات إدارة الذاكرة؟ الجواب واضح: ليست هناك حاجة لهذا. لذلك ، يمكننا أن نحاول المضي قدمًا من حقيقة أننا يجب ألا نخزن هذه المعلومات: يمكننا أن نذهب إلى مجموعة خطية ، ونعرف حجم كل كائن ونقوم بتحريك المؤشر في كل مرة حسب حجم الكائن التالي.
لا توجد هياكل بيانات إضافية على الكومة التي تحتوي على مؤشرات لكل كائن يتحكم به الكومة.
ومع ذلك ، عندما لا نحتاج إلى ذاكرة ، يجب أن نحررها. وعند تحرير الذاكرة ، يصبح من الصعب علينا الاعتماد على الممر الخطي للكومة: إنها طويلة وغير فعالة. نتيجة لذلك ، توصلنا إلى استنتاج مفاده أننا نحتاج إلى تخزين معلومات بطريقة ما عن مناطق الذاكرة الخالية.
كومة الذاكرة المؤقتة لديها قوائم الذاكرة الحرة.
إذا قررنا ، كما قررنا ، تخزين معلومات حول المناطق الحرة ، وأثناء تحرير الذاكرة ، كانت هذه المساحات صغيرة جدًا ، ثم قبل كل شيء واجهنا مشكلة تخزين المعلومات حول المناطق الحرة التي واجهناها عند التفكير في المناطق المحتلة (إذا على جانبي الكائن المحتل واحد تم تحريره ، من أجل تخزين المعلومات حوله ، فمن الضروري في أسوأ الحالات 2/3 من حجمها. المؤشر + الحجم مقابل SyncBlockIndex + VMT + أي حقل - في حالة الكائن). هذا يبدو مضيعة للوقت ، يجب عليك الاعتراف: ليس من حسن الحظ دائمًا تحرير مجموعة من الكائنات التي تتبع بعضها البعض. عادة ، يتم إطلاق سراحهم بطريقة فوضوية. ولكن على عكس المواقع المزدحمة ، التي لا نحتاج إلى البحث فيها خطيًا ، نحتاج إلى البحث عن مواقع مجانية لأننا عندما نخصص ذاكرة ، فقد نحتاج إليها مرة أخرى. لذلك ، تنشأ رغبة طبيعية تمامًا للحد من التفتت والضغط على الكومة ، ونقل جميع المناطق المشغولة إلى أماكن خالية ، وبالتالي تشكيل مساحة كبيرة من المنطقة الحرة حيث يمكن تخصيص الذاكرة.
هذا هو المكان الذي تأتي منه فكرة خوارزمية الضغط.
لكن انتظر ، أنت تقول. بعد كل شيء ، يمكن أن تكون هذه العملية صعبة للغاية. تخيل أنك قمت بتحرير كائن في بداية الكومة. وماذا تقول ، هل تحتاج إلى تحريك كل شيء؟ حسنًا ، بالطبع ، يمكنك أن تحلم بموضوع تعليمات ناقل وحدة المعالجة المركزية ، والتي يمكنك استخدامها لنسخ مساحة كبيرة من الذاكرة المشغولة. ولكن هذه ليست سوى بداية العمل. يجب أيضًا إصلاح جميع المؤشرات من حقول الكائنات إلى الكائنات التي خضعت لحركات. هذه العملية يمكن أن تستغرق وقتًا طويلاً. لا ، يجب أن ننطلق من شيء آخر. على سبيل المثال ، عن طريق تقسيم الجزء بالكامل من ذاكرة الكومة إلى قطاعات والعمل معها بشكل منفصل. إذا عملنا بشكل منفصل في كل قطاع (من أجل التوقع وقياس هذه القدرة على التنبؤ - ويفضل أن يكون ذلك ، أحجام ثابتة) ، فإن فكرة الضغط لا تبدو ثقيلة للغاية: إنها كافية لضغط قطاع واحد ومن ثم يمكنك البدء في الحديث عن الوقت الذي يستغرقه ضغط أحد هذه القطاعات .
الآن يبقى أن نفهم على أي أساس التقسيم إلى قطاعات. هنا يجب أن ننتقل إلى التصنيف الثاني ، والذي يتم تقديمه على النظام الأساسي: مشاركة الذاكرة ، بناءً على العمر الافتراضي لعناصرها الفردية.
يكون التقسيم بسيطًا: إذا أخذنا في الاعتبار أننا سنخصص الذاكرة مع زيادة العناوين ، فإن الكائنات المحددة الأولى تصبح الأقدم ، وتصبح الكائنات الموجودة في العناوين العليا هي الأصغر. علاوة على ذلك ، كونك ذكيًا ، يمكنك التوصل إلى استنتاج مفاده أن الكائنات في التطبيقات تنقسم إلى مجموعتين: تلك التي تم إنشاؤها لحياة طويلة وتلك التي تم إنشاؤها للعيش قليلاً جدًا. على سبيل المثال ، لتخزين المؤشرات مؤقتًا إلى كائنات أخرى في شكل مجموعة. أو نفس الكائنات DTO. وفقًا لذلك ، من وقت لآخر ، نضغط على حفنة نحصل على عدد من الأشياء الطويلة العمر - في العناوين السفلية وعدد من الأشياء القصيرة - في كبار السن.
وهكذا تلقينا الأجيال .
بتقسيم الذاكرة إلى أجيال ، تتاح لنا الفرصة للنظر بشكل أقل في أغراض الجيل الأقدم ، التي أصبحت أكثر فأكثر.
ولكن هناك سؤال آخر يطرح نفسه: إذا كان لدينا جيلان فقط ، فسنواجه مشكلات. أو سنحاول جعل GC تعمل بسرعة بدون قناع: ثم بحجم الجيل الأصغر سنحاول القيام بالحد الأدنى للحجم. نتيجة لذلك ، ستفشل الكائنات بطريق الخطأ في الجيل الأقدم (إذا كان GC يعمل "في الوقت الحالي ، أثناء تخصيص غاضب للذاكرة للعديد من الكائنات"). أو لتقليل الفشل العرضي ، سنزيد من حجم جيل الشباب. بعد ذلك ، سيعمل GC على الجيل الأصغر سناً لفترة كافية ، مما يؤدي إلى إبطاء التطبيق وإبطائه.
المخرج هو مقدمة الجيل "المتوسط". المراهقة. بمعنى آخر ، إذا كنت تعيش حتى سن المراهقة ، فمن الأرجح أن تعيش حتى سن الشيخوخة. يكمن جوهر المقدمة في تحقيق التوازن بين الحصول على أصغر جيل أصغر سنا وأكثر جيل أقدم ثباتًا ، حيث من الأفضل عدم لمس أي شيء. هذه منطقة لم يتم تحديد مصير الكائنات فيها بعد. يتم إنشاء الجيل الأول (لا تنس ما نفكر فيه من الصفر) أيضًا صغيرًا ويبدو GC أقل في كثير من الأحيان. وبالتالي ، يسمح GC للكائنات الموجودة في الجيل الأول المؤقت ، بعدم الدخول إلى الجيل القديم ، وهو أمر يصعب للغاية جمعه.
لذلك لدينا فكرة ثلاثة أجيال.
الطبقة التالية من التحسين هي محاولة لرفض الضغط. بعد كل شيء ، إذا لم تقم بذلك ، فنحن نتخلص من طبقة ضخمة من العمل. دعنا نعود إلى مسألة المواقع المجانية.
إذا قمنا بعد استخدام كل الذاكرة المتوفرة في الكومة ، وتم استدعاء GC ، فهناك رغبة طبيعية في رفض الضغط لصالح تخصيص مزيد من الذاكرة داخل الأقسام المحررة إذا كان حجمها يكفي لاستيعاب عدد معين من الكائنات. هنا نأتي إلى فكرة خوارزمية ثانية لتحرير الذاكرة في GC ، تسمى Sweep
: نحن لا نضغط الذاكرة ، نحن نستخدم الفراغات من الكائنات المحررة لوضع كائنات جديدة
لذلك وصفنا وبررنا جميع أساسيات خوارزميات GC.
لذلك ، بعد يومين ، يمكننا استخلاص بعض الاستنتاجات. كما أفهمها ، فإن معظم الناس يفهمون معظم النص ، أو حتى الكل. أجاب بعض الناس أنهم لم يفهموا ، والبعض الآخر ، على التوالي ، فهم جزئيا. وفاز الخلاف بفريق من القراء ، وإن كان بفارق ضئيل ، كما يقولون. ولكن ، كما قلت ، سيستفيد الجميع من هذا: سيتم تعديل النص واستكماله. بالإضافة إلى ذلك ، تم تحديثه في كلا المكانين: سواء في الكتاب أو هنا ، في المقالة.
