تعرّف كيف يؤثر جامع النفايات (GC)، ونموذج الملكية، والعد المرجعي على السرعة والكمون والأمان — وكيف تختار لغة تناسب أهدافك.

إدارة الذاكرة هي مجموعة القواعد والآليات التي يستخدمها البرنامج لطلب الذاكرة، استخدامها، ثم إعادتها. كل برنامج يعمل يحتاج إلى ذاكرة لأشياء مثل المتغيرات، بيانات المستخدم، مخازن الشبكة، الصور، والنتائج الوسيطة. لأن الذاكرة محدودة ومشتركة مع نظام التشغيل وتطبيقات أخرى، يجب على اللغات أن تقرر من مسؤول عن تحريرها ومتى يحدث ذلك.
تؤثر هذه القرارات في نتيجتين يهتم بهما معظم الناس: مدى "سلاسة" وأداء البرنامج، ومدى اعتمادية سلوكه تحت الضغط.
الأداء ليس رقمًا واحدًا. إدارة الذاكرة يمكن أن تؤثر على:
لغة تقوم بالتخصيص بسرعة لكنها أحيانًا تتوقف للتنظيف قد تبدو رائعة في المقاييس لكنها قد تشعر بالخِشْخشة في التطبيقات التفاعلية. نموذج آخر يتجنب التوقفات قد يتطلب تصميمًا أدق لتجنب التسريبات وأخطاء أمد الحياة.
الأمان يتعلق بمنع الأخطاء المتعلقة بالذاكرة، مثل:
العديد من قضايا الأمان الشهيرة تعود إلى أخطاء ذاكرة مثل use-after-free أو تجاوز المخازن.
يقدّم هذا الدليل جولة غير تقنية في نماذج الذاكرة الرئيسية المستخدمة في لغات شائعة، وما الذي تحسّنه، والمقايضات التي تقبلها عند اختيارك لأحدها.
الذاكرة هي المكان الذي يحتفظ فيه برنامجك بالبيانات أثناء التشغيل. معظم اللغات تنظم ذلك حول منطقتين رئيسيتين: المكدس والكومة.
فكر بالمكدس ككومة من الملاحظات اللاصقة للدورة الجارية. عندما تبدأ دالة، تحصل على "إطار" صغير على المكدس لمتغيراتها المحلية. عندما تنتهي الدالة، يُزال ذلك الإطار دفعة واحدة.
هذا سريع ومتوقع—لكن يعمل فقط للقيم التي يُعرف حجمها وينتهي عمرها مع استدعاء الدالة.
الكومة أشبه بغرفة تخزين حيث يمكنك الاحتفاظ بالكائنات طالما تحتاج إليها. رائعة للأشياء مثل القوائم ذات الحجم المتغير، النصوص، أو الكائنات المشتركة عبر أجزاء مختلفة من البرنامج.
نظرًا لأن كائنات الكومة قد تعيش بعد نهاية دالة واحدة، يصبح السؤال الرئيسي: من المسؤول عن تحريرها، ومتى؟ تلك المسؤولية هي "نموذج إدارة الذاكرة" في اللغة.
المؤشر أو مرجع هو طريقة للوصول إلى كائن بشكل غير مباشر—مثل أن يكون لديك رقم الرف لصندوق في غرفة التخزين. إذا أُتلف الصندوق لكن لديك رقم الرف، قد تقرأ بيانات مُلوَّثة أو يتعطّل البرنامج (خطأ استخدام بعد التحرير).
تخيّل حلقة تنشئ سجل عميل، تُنسّق رسالة، ثم تتخلص منها:
بعض اللغات تخفي هذه التفاصيل (تنظيف تلقائي)، بينما تكشفها أخرى (تحرر الذاكرة صراحةً، أو يجب أن تتبع قواعد الملكية). يستكشف بقية المقال كيف تؤثر هذه الاختيارات على السرعة، التوقفات، والسلامة.
الإدارة اليدوية تعني أن البرنامج (وبالتالي المطور) يطلب الذاكرة صراحةً ثم يطلقها لاحقًا. عمليًا يظهر ذلك في malloc/free في C، أو new/delete في C++. لا تزال شائعة في برمجة الأنظمة حيث تحتاج إلى تحكم دقيق بمتى تُكتسب الذاكرة وتُعاد.
تخصّص عادةً عندما يجب أن يعيش كائن بعد انتهاء استدعاء الدالة الحالية، أو عندما ينمو ديناميكيًا (مثل مخزن قابل لإعادة القياس)، أو عندما تحتاج إلى تخطيط معين للتوافق مع الأجهزة أو نظام التشغيل أو بروتوكولات الشبكة.
بدون جامع نفايات يعمل في الخلفية، تقل المفاجآت من حيث التوقفات. يمكن جعل التكلفة في التخصيص والتحرير متوقعة للغاية، خاصة عند إقرانها بمخصصات مخصصة، مجمّعات، أو مخازن ثابتة الحجم.
يمكن للتحكم اليدوي أن يقلل أيضًا من العبء: لا توجد مرحلة تتبع، ولا حاجات لحواجز الكتابة، وغالبًا بيانات وصفية أقل لكل كائن. عندما يُصمّم الكود بعناية، يمكنك تلبية أهداف الكمون الضيقة والحفاظ على استخدام الذاكرة ضمن حدود صارمة.
المقايضة هي أن البرنامج قد يرتكب أخطاء لن يمنعها زمن التشغيل تلقائيًا:
هذه الأخطاء قد تسبب تعطلًا، فسادًا في البيانات، وثغرات أمنية.
تقلل الفرق الخطر بتضييق الأماكن التي يُسمح فيها بالتخصيص الخام والاعتماد على أنماط مثل:
std::unique_ptr) لترميز الملكيةالإدارة اليدوية غالبًا خيار قوي للبرمجيات المدمجة، أنظمة الزمن الحقيقي، مكونات نظم التشغيل، والمكتبات الحساسة للأداء—أماكن حيث يهم التحكم الضيق والكمون المتوقع أكثر من راحة المطور.
جمع النفايات (GC) هو تنظيف تلقائي للذاكرة: بدلاً من أن تطلب أنت free بنفسك، يتتبع زمن التشغيل الكائنات ويستعيد تلك التي لم تعد قابلة للوصول. عمليًا يعني هذا أنه يمكنك التركيز على السلوك وتدفق البيانات بينما يتعامل النظام مع معظم قرارات التخصيص والتحرير.
معظم المجمّعات تعمل بتحديد الكائنات الحية أولًا، ثم استعادة الباقي.
الجامع التعاقبي (Tracing GC) يبدأ من "الجذور" (مثل متغيرات المكدس والمراجع العالمية والسجلات)، يتتبّع المراجع لتمييز كل شيء القابل للوصول، ثم يجتاح الكومة لتحرير الكائنات غير المعلمة. إذا لم يشِر شيء إلى كائن، يصبح مؤهلاً للجمْع.
GC الجيلي (Generational GC) مبني على ملاحظة أن العديد من الكائنات تموت مبكرًا. يفرق الكومة إلى أجيال ويجمع المنطقة الشابة بتكرار، وهذا عادة أرخص ويحسّن الكفاءة العامة.
GC المتزامن (Concurrent GC) يشغل أجزاء من الجمع جنبًا إلى جنب مع سلاسل التطبيق، بهدف تقليل التوقفات الطويلة. قد يتطلب ذلك مزيدًا من تتبُّع الحالة للحفاظ على الاتساق بينما يظل البرنامج قيد التشغيل.
GC عادةً ما يبادل التحكم اليدوي بـ عمل وقت التشغيل. بعض الأنظمة تُعطي أولوية للإنتاجية المستقرة (إنجاز الكثير من العمل في الثانية) لكنها قد تُدخل توقفات "أوقف العالم". أنظمة أخرى تقلل التوقفات لتطبيقات حساسة للكمون لكنها قد تضيف مصاريف أثناء التنفيذ العادي.
GC يزيل فئة كاملة من أخطاء أمد الحياة (خصوصًا use-after-free) لأن الكائنات لا تُستعاد بينما تظل قابلة للوصول. كما يقلل تسريبات الذاكرة الناتجة عن نسيان التحري...
يمكنك أيضًا تجميع لا تزال بالإمكان "التسريب" عن طريق الاحتفاظ بالمراجع لفترة أطول من اللازم. في قواعد كود كبيرة حيث يكون تتبع الملكية صعبًا يدويًا، غالبًا ما يُسرّع GC دورة التطوير.
بيئات زمن التشغيل ذات GC شائعة في JVM (Java, Kotlin)، .NET (C#, F#)، Go، ومحركات JavaScript في المتصفحات وNode.js.
العد المرجعي هو استراتيجية حيث يتتبع كل كائن كم "مالك" (مراجع) يُشير إليه. عندما يصل العداد إلى صفر، يُحرر الكائن فورًا. يبدو هذا بديهياً: بمجرد ألا يصل شيء للكائن، تستعاد ذاكرته.
في كل مرة تنسخ أو تخزن مرجعًا، يزيد زمن التشغيل عدّاد الكائن؛ وعندما يختفي مرجع، يُنقص العداد. بلوغ الصفر يُشغّل التنظيف فورًا.
هذا يجعل إدارة الموارد مباشرة: الكائنات غالبًا ما تُحرر قريبًا من لحظة توقفك عن استخدامها، ما قد يقلل من ذروة الذاكرة ويجنب الإرجاء في الاستعادة.
العد المرجعي يميل إلى أن يكون له تكلفة ثابتة ومستمرة: عمليات الزيادة/النقص تحدث في العديد من التعيينات واستدعاءات الدوال. هذه التكلفة غالبًا صغيرة، لكنها حاضرة دائمًا.
الميزة أنك غالبًا لا تحصل على توقفات عالمية كبيرة كما في بعض جامعات التتبع. الكمون يكون أكثر سلاسة، رغم أن دفعات تحرر كبيرة قد تحدث عندما تفقد رسوم كبيرة من الكائنات مالكها الأخير.
العد المرجعي لا يستطيع استعادة الكائنات المشارك بها في دورة. إذا A تشير إلى B وB تشير إلى A، تبقى جميع الأعدّاد فوق الصفر حتى لو لم يكن هناك ما يشير إليهما من خارج الدورة—فتتكوّن تسريبات.
تتعامل الأنظمة مع هذا بطرق منها:
الملكية والاقتراض هو نموذج ذاكرة مرتبط بشكل وثيق بـ Rust. الفكرة بسيطة: يجبر المجمّع قواعد تجعل من الصعب إنشاء مؤشرات معلقة، أو تحرير مزدوج، والعديد من سباقات البيانات—دون الاعتماد على جامع نفايات في وقت التشغيل.
كل قيمة لها "مالك" واحد بالضبط في أي وقت. عندما يخرج المالك عن النطاق، يُحرر القيمة فورًا وبشكل متوقع. هذا يمنحك إدارة موارِد حتمية (ذاكرة، مقابس ملفات، سوكيتات) مشابهة للتنظيف اليدوي، لكن مع طرق أقل للخطأ.
يمكن أن تنتقل الملكية أيضًا: التعيين إلى متغير جديد أو تمرير القيمة إلى دالة يمكن أن ينقل المسؤولية. بعد النقل، لا يمكن استخدام الارتباط القديم، مما يمنع الاستخدام بعد التحرير بحكم التصميم.
الاقتراض يتيح لك استخدام قيمة دون أن تصبح مالكًا لها.
الاقتراض المشترك يسمح بالوصول للقراءة فقط ويمكن نسخه بحرية.
الاقتراض القابل للتعديل يسمح بالتحديث، لكنه يجب أن يكون حصريًا: بينما يوجد، لا يجوز لشيء آخر قراءة أو كتابة نفس القيمة. تُفحَص هذه القاعدة "كاتب واحد أو عدة قرّاء" أثناء الترجمة.
لأن أمد الحياة متتبَّعة، يمكن للمترجم رفض الكود الذي قد يعيش بعد البيانات التي يشير إليها، مما يقضي على العديد من أخطاء المؤشرات المعلقة. نفس القواعد تمنع أيضًا فئة كبيرة من سباقات البيانات في الكود المتزامن.
المقايضة هي منحنى تعلم وقيود تصميمية. قد تحتاج إلى إعادة هيكلة تدفقات البيانات، وضع حدود ملكية أو استخدام أنواع متخصصة للحالة المشتركة القابلة للتعديل.
هذا النموذج مناسب لبرمجيات الأنظمة—الخدمات، المدمج، الشبكات، والمكونات الحساسة للأداء—حيث تريد تنظيفًا متوقعًا وكمونًا منخفضًا دون توقفات GC.
عندما تنشئ الكثير من الكائنات قصيرة العمر—عقد شجرة بناء الجملة في محلل، كائنات إطار اللعبة، بيانات مؤقتة أثناء طلب ويب—يمكن أن يهيمن عبء تخصيص وتحرير كل كائن على وقت التشغيل. الساحات (المعروفة أيضًا بالمناطق) والمخازن أنماط تتبادل عمليات التحري الدقيقة بتحكم جماعي سريع.
الساحة هي "منطقة" ذاكرة تخصص إليها العديد من الكائنات بمرور الوقت، ثم تُحرَّر كافةُها مرة واحدة عن طريق إسقاط أو إعادة ضبط الساحة.
بدلاً من تتبع عمر كل كائن على حدة، تربط أمد الحياة بحد واضح: "كل ما تخصّص لهذا الطلب" أو "كل ما تخصّص أثناء تجميع هذه الدالة".
الساحات غالبًا سريعة لأنها:
هذا يحسّن الإنتاجية، وقد يقلل أيضًا من تقلب الكمون الناتج عن عمليات تحرير متكررة أو تنازع على المُخصّص.
تظهر الساحات والمخازن في:
القاعدة الرئيسية بسيطة: لا تدع المراجع تهرب من المنطقة المالكة للذاكرة. إذا تم تخزين شيء تم تخصيصه في ساحة في مكان عام أو إرجاعه بعد عمر الساحة، فإنك تعرض نفسك لأخطاء use-after-free.
تتعامل اللغات والمكتبات مع هذا بطرق مختلفة: بعضُها يعتمد على الانضباط والواجهات البرمجية، والبعض الآخر يستطيع ترميز حدّ المنطقة ضمن الأنواع.
الساحات والمخازن ليست بديلًا لجمع النفايات أو النظام القائم على الملكية—غالبًا ما تكون مكمّلة. لغات GC تستخدم مجموعات للأجزاء الساخنة؛ لغات الملكية يمكن أن تستخدم الساحات لتجميع التخصيصات وجعل أعمارها صريحة. عند الاستخدام الحذر، تُنتج "التخصيص السريع افتراضيًا" دون التخلي عن الوضوح حول زمن تحرير الذاكرة.
نموذج الذاكرة في اللغة هو جزء من قصة الأداء والأمان فقط. المترجمات الحديثة وزمن التشغيل يعيدون كتابة برنامجك لتقليل التخصيص، التحرير المبكر، وتفادي التعقيدات الإضافية. لهذا السبب تحطّم القواعد العامة مثل "GC بطيء" أو "اليدوي هو الأسرع" في التطبيقات الحقيقية.
العديد من التخصيصات موجودة فقط لتمرير البيانات بين الدوال. من خلال تحليل الهروب يمكن للمترجم الإثبات أن الكائن لا يَهرب من النطاق الحالي ويحتفظ به على المكدس بدلًا من الكومة.
هذا يمكن أن يزيل تخصيصًا على الكومة بالكامل، جنبًا إلى جنب مع التكاليف المرتبطة (تعقب GC، تحديثات العد المرجعي، أقفال المُخصّص). في اللغات المُدارة، هذا سبب رئيسي يجعل الكائنات الصغيرة أرخص مما تتوقع.
عندما يقوم المترجم بالملء (استبدال استدعاء الدالة بجسمها)، قد يرى "خلف" طبقات التجريد. تلك الرؤية تُمكن تحسينات مثل:
واجهات برمجة تطبيقات مصممة جيدًا يمكن أن تصبح "صفر تكلفة" بعد هذه التحسينات، حتى لو بدت مكثفة التخصيص في المصدر.
JIT يمكنه تحسين اعتمادًا على بيانات الإنتاج الحقيقية: أي المسارات ساخنة، أحجام الكائنات النموذجية، وأنماط التخصيص. هذا غالبًا ما يحسن الإنتاجية لكنه قد يضيف وقت تدفئة وتوقفات عرضية لإعادة الترجمة أو GC.
الترجمة المسبقة يجب أن تخمن أكثر من البداية، لكنها تقدم وقت بدء متوقعًا وكمونًا أكثر ثباتًا.
بيئات GC تعرض إعدادات مثل حجم الهيب، أهداف زمن التوقف، وعَتبات الأجيال. عدّلها عندما تملك دليلًا مقاسًا (مثل قفزات في الكمون أو ضغط الذاكرة)، وليس كخطوة أولى.
تنفيذان "لنفس" الخوارزمية يمكن أن يختلفا في أعداد التخصيص الخفية، الكائنات المؤقتة، وقفز المؤشرات. تلك الاختلافات تتفاعل مع المحسّنات، المُخصّص، وسلوك الكاش—لذلك مقارنة الأداء تتطلب قياسًا وليس افتراضات.
خيارات إدارة الذاكرة لا تُغير فقط كيفية كتابة الكود—بل تغيّر متى يحدث العمل، كم الذاكرة التي تحتاجها، وكيف يبدو الأداء للمستخدمين.
الإنتاجية تعني "كمية العمل لكل وحدة زمن". تخيّل مهمة دفعة مسائية تعالج 10 ملايين سجل: إذا أضاف GC أو العد المرجعي عبءًا صغيرًا لكنه سهل على المطور، قد تُنجز أسرع إجمالًا.
الكمون يعني "كم يستغرق إجراء واحد من البداية للنهاية". لطلب ويب، استجابة بطيئة واحدة تضر بتجربة المستخدم حتى لو أن الإنتاجية متوسطة مرتفعة. زمن تشغيل يتوقف أحيانًا لجمع الذاكرة قد يكون مقبولًا للمعالجة الدفعيّة، لكنه ملحوظ للتطبيقات التفاعلية.
بصمة ذاكرة أكبر تزيد من تكاليف السحابة ويمكن أن تبطئ البرامج. عندما لا يتناسب مجموعة العمل مع كاش المعالج، ينتظر CPU كثيرًا لبيانات من الذاكرة. بعض الاستراتيجيات تتبادل مزيدًا من الذاكرة مقابل السرعة (مثل الاحتفاظ بالكائنات المحررة في مخازن)، بينما تقلل أخرى الذاكرة لكنها تضيف تتبُّعًا.
التجزؤ يحدث عندما تتجزأ الذاكرة الحرة إلى فجوات صغيرة—مثل محاولة صف سيارة فان في موقف به مساحات صغيرة متناثرة. قد يقضي المُخصّص وقتًا أطول في البحث عن مساحة، وقد ينمو استهلاك الذاكرة رغم وجود مساحة كافية.
محلية الكاش تعني أن البيانات ذات العلاقة قريبة من بعضها. تخصيصات المجموعات/الساحات غالبًا تحسّن المحلية، بينما الكومة الطويلة العمر ذات أحجام متباينة قد تنزلق إلى ترتيب أقل ملاءمة للكاش.
إذا كنت تحتاج إلى أزمنة استجابة متسقة—ألعاب، تطبيقات صوت، أنظمة تداول، مضبّطات مدمجة أو زمن حقيقي—فـ"سريع غالبًا ولكن أحيانًا بطيء" أسوأ من "أبطأ قليلًا لكن متسق." هنا تبرز أنماط التحرير المتوقعة والتحكم الضيق في التخصيص.
أخطاء الذاكرة ليست مجرد "زلات مبرمج". في كثير من النظم الحقيقية تتحول إلى مشاكل أمنية: تعطلات مفاجئة (حرمان من الخدمة)، كشف بيانات عرضي (قراءة ذاكرة محررة أو غير مهيأة)، أو شروط يمكن للمهاجمين استغلالها لتشغيل تعليمات غير متوقعة.
تختلف طرق فشل إدارة الذاكرة باختلاف الاستراتيجيات:
التزامن يغير نموذج التهديد: الذاكرة "الصالحة" في خيط واحد قد تصبح خطيرة عندما يقوم خيط آخر بتحريرها أو تعديلها. النماذج التي تفرض قواعد حول المشاركة (أو تتطلب تزامنًا صريحًا) تقلل احتمال حدوث سباقات تؤدي إلى فساد الحالة أو تسريبات أو تعطلات متقطعة.
لا يُلغي أي نموذج ذاكرة كل المخاطر—الأخطاء المنطقية (أخطاء المصادقة، الإعدادات غير الآمنة، التحقق الخاطئ) لا تزال ممكنة. الفرق القوية تضع طبقات حماية: معالجات الكشف أثناء الاختبار، مكتبات معيارية آمنة، مراجعات كود صارمة، fuzzing، وحدود صارمة حول الكود غير الآمن/الاستدعاء عبر واجهات FFI. سلامة الذاكرة هي تقليل كبير لمساحة الهجوم، وليست ضمانًا مطلقًا.
مشكلات الذاكرة أسهل إصلاحًا عندما تُكتشف قريبًا من التغيير الذي أدخلها. المفتاح هو القياس أولًا، ثم تضييق نطاق المشكلة بالأداة المناسبة.
ابدأ بتحديد ما إذا كنت تطارد السرعة أم نمو الذاكرة.
من أجل الأداء، قِس الوقت الفعلي، وقت المعالج، معدل التخصيص (بايت/ثانية)، ووقت GC أو المُخصّص. بالنسبة للذاكرة، تتبع ذروة RSS، RSS المستقر، وعدد الكائنات مع الزمن. شغل نفس الحمل مع مدخلات ثابتة؛ التغيرات الصغيرة قد تُخفي تغيرات تخصيص.
علامات شائعة: طلب واحد يخصص أكثر مما تتوقع، أو الذاكرة ترتفع مع المرور رغم ثبات الإنتاجية. الإصلاحات غالبًا تشمل إعادة استخدام المخازن المؤقتة، الانتقال إلى تخصيص بالساحات/المخازن للكائنات قصيرة العمر، وتبسيط رسوم الكائنات كي تقل الأشياء التي تعيش عبر دورات الجمع.
أعد الإنتاج بمدخل بسيط، فعّل أقسى الفحوصات الزمنية (sanitizers/GC verification)، ثم التقط:
اعتبر الإصلاح الأول تجربة؛ أعد القياسات لتتأكد أن التغيير خفّض التخصيصات أو استقرار الذاكرة—دون نقل المشكلة إلى مكان آخر. للمزيد عن تفسير المقايضات، راجع /blog/performance-trade-offs-throughput-latency-memory-use.
اختيار لغة ليس فقط مسألة ترميز أو نظام بيئي—نموذج الذاكرة يشكّل سرعة التطوير اليومي، المخاطر التشغيلية، وكيفية توقع الأداء تحت حمل حقيقي.
طابق احتياجات المنتج لاستراتيجية الذاكرة بالإجابة عمّا يلي:
إذا كنت تغيّر النموذج، خطّط لاحتكاك: استدعاء المكتبات الحالية (FFI)، اتفاقيات الذاكرة المختلطة، الأدوات، وسوق التوظيف. النماذج الأولية تكشف التكاليف الخفية (التوقفات، نمو الذاكرة، استستهلاك CPU) أبكر.
نهج عملي هو بناء نموذج رقيق لنفس الميزة في البيئات التي تفكر بها ومقارنة معدل التخصيص، ذيل الكمون، والذاكرة القصوى تحت حمل تمثيلي. تعمل فرق أحيانًا تقييمات "تفاح-لتفاح" في Koder.ai: يمكنك بسرعة إعداد واجهة React صغيرة مع backend بـ Go + PostgreSQL، ثم تكرار أشكال الطلبات لترى كيف يتصرف خدمة مبنية على GC تحت ضغط حقيقي (وتصدّر الشيفرة عند الاستعداد للمزيد).
حدد أعلى 3–5 قيود، ابنِ نموذجًا رقيقًا، وقِس استخدام الذاكرة، ذيل الكمون، وأنماط الفشل.
| النموذج | السلامة افتراضيًا | قابلية توقع الكمون | سرعة المطور | المساوئ الشائعة |
|---|---|---|---|---|
| اليدوي | منخفض–متوسط | عالي | متوسط | تسريبات، استخدام بعد التحرير |
| GC | عالي | متوسط | عالي | توقفات، نمو الهيب |
| العد المرجعي | متوسط–عالي | عالي | متوسط | دورات، تكلفة التعيين |
| الملكية | عالي | عالي | متوسط | منحنى تعلم |
إدارة الذاكرة هي كيفية تخصيص البرنامج للذاكرة للبيانات (مثل الكائنات، السلاسل، المخازن المؤقتة) ثم تحريرها عندما لم تعد مطلوبة.
تؤثر على:
المكدس سريع، تلقائي ومرتبط باستدعاءات الدوال: عندما تنتهي الدالة، يُزال إطار المكدس الخاص بها دفعة واحدة.
الكومة مرنة للبيانات الديناميكية أو طويلة العمر، لكنها تحتاج إلى استراتيجية لتحديد متى ومن يقوم بتحريرها.
قاعدة عملية: المكدس مناسب للمتغيرات المحلية قصيرة العمر وثابتة الحجم؛ الكومة تُستخدم عندما تكون أمد الحياة أو الأحجام غير متوقعة.
المرجع/المؤشر يتيح للوحدة الوصول إلى كائن بشكل غير مباشر. الخطر يظهر عندما تُحرر ذاكرة الكائن بينما لا يزال هناك مرجع يشير إليه.
هذا يمكن أن يؤدي إلى:
تعني الإدارة اليدوية للذاكرة أنك تقوم صراحة بطلب الذاكرة ثم تحريرها (مثل malloc/free أو new/delete).
تُستخدم عندما تحتاج إلى:
الثمن: زيادة مخاطر الأخطاء إذا لم تُدَار الملكية وأمد الحياة بعناية.
يمكن أن تكون الإدارة اليدوية سريعة جدًا وقابلة للتنبؤ إذا صُممت الوحدة بشكل جيد، لأنّ ليس هناك دورة خلفية لجامع نفايات قد تُوقِف التنفيذ.
يمكن تحسينها باستخدام:
لكن من السهل أيضًا خلق أنماط مكلفة (التجزؤ، تنافس المُخصص، الكثير من عمليات تخصيص/تحرير الصغيرة).
جمع النفايات (GC) يحدد الكائنات غير القابلة للوصول ويستعيد ذاكراتها تلقائيًا.
معظم المجمِعات التعقبية تعمل كالتالي:
هذا يحسن الأمان (يقلل من أخطاء الاستخدام بعد التحرير) لكنه يضيف عملًا زمنياً وقد يسبب توقفات حسب تصميم الجامع.
العد المرجعي يحرر الكائن عندما يصل عداده (عدد المراجع) إلى صفر.
الإيجابيات:
السلبيات:
تُستخدم مراجع ضعيفة أو كاشفات دورية للدورات للتخفيف من المشكلة.
الملكية والاقتراض (نموذج مشابه لـ Rust) يستخدم قواعد تُفرض وقت الترجمة لتفادي أخطاء أمد الحياة دون الاعتماد على GC وقت التشغيل.
أفكار أساسية:
يوفر هذا تنظيفًا متوقعًا دون توقفات GC، لكنه قد يتطلب إعادة هيكلة تدفق البيانات لإرضاء قواعد المصحح.
المنطقة/الساحة (arena/region) تخصص العديد من الكائنات في "منطقة" ثم تُحررها جميعًا دفعة واحدة عند إعادة تعيين المنطقة.
مفيدة عندما يكون لديك حد عمر واضح، مثل:
القاعدة الأمنية: لا تسمح لمراجع أن تهرب خارج عمر المنطقة.
ابدأ بقياسات حقيقية تحت حمولة تمثيلية:
استخدم أدوات مستهدفة: ملفات التخصيص/CPU، لقطات الذاكرة، كاشفات التسرب، المعقِبات/المجزِّئات. عدّل إعدادات وقت التشغيل (مثل معلمات GC) فقط بعد تحديد المشكلة بقياسات.