١٥ ديسمبر ٢٠٢١

جمع القمامة (Garbage Collection)

تتم عملية ادارة الذاكرة في الJavaScript بطريقة تلقائية غير مرئية نحن ننشئ لبقيم البسيطة, الكائنات , الدوال… و كل ذلك يستهلك من الذاكرة.

ما الذي سيحدث اذا لم نعد بحاجة لأحدهم ؟ كيف يكتشفها محرك ال JavaScript و يتخلص منها ؟

قابلية الوصول

المفهوم الأساسي لعملية ادارة الذاكرة في ال JavaScript هو قابلية الوصول.

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

  1. هناك بعض القيم الأساسية التي يمكن الوصول اليها دائماو لا يمكن مسحها لأسباب واضحة. علي سبيل المثال:

    • المتغيرات المحلية و المعاملات للدالة التي يتم استخدامها
    • المتغيرات و معاملات الدوال في سلسلة متصلة من الاستدعاءات
    • المتغيرات العامة
    • (هنالك المزيد, بعضهم داخلي)

    هذه القيم تسمي ب الجذور (roots).

  2. أي قيمة اخري يمكن اعتبارها قابلة للوصول اليها اذا ما كان يمكن الوصول اليها بالفعل من جذر عن طريق مرجع(reference) او مجموعة من المراجع.

    علي سبيل المثال, اذا كان هناك كائن مخزن في متغير محلي, و الكائن به خاصية مرجه لكائن اخر, يمكن اعتبار هذا الكائن انه يمكن الوصول اليه, و يمكن الوصول ايضا الي كل مراجع هذا الكائن, كما يتم شرحه في المثال التالي.

تحدث عملية خلفية في محرك ال JavaScript يسمي ب جامع القمامة garbage collector يتم خلالها مراقبة كل الكائنات و ازالة الكائنات التي لا يمكن الوصول اليها.

مثـــال بـسـيـط

هذا هوا ابسط مثـال:

// user له مرجع الي الكائن
let user = {
  name: "John"
};

السهم هنا يمثل مرجع لكائن, المتغير العام "user" يرجع الي الكائن {name: "John"} (الذي سنطلق عليه جون اختصارا). خاصية "name" في كائن جون تخزن قيمة بسيطة لذا تم رسمها داخل الكائن.

اذا تم استخدام اسم المتغير user مرة اخري , هنا يتم فقدان مرجع الكائن:

user = null;

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

مرجعين

تصور الأن اننا نسخنا مرجع user الي admin:

// user له مرجع الي الكائن
let user = {
  name: "John"
};

let admin = user;

Now if we do the same:

user = null;

…في هذه الحالة ما زال بالامكان الوصول الي الكائن عن طريق المتغير العام admin, لذا فهو مخزن بالذاكرة. اذا تم استخدام المتغير admin في مكان أخر ايضا هنا يمكن ازالة الكائن .

الكائنات المترابطة

Now a more complex example. The family: و الان الي مثال أكثر تعقيدا, كل العائلة:

function marry(man, woman) {
  woman.husband = man;
  man.wife = woman;

  return {
    father: man,
    mother: woman
  }
}

let family = marry({
  name: "John"
}, {
  name: "Ann"
});

الدالة marry “تزوج” كائنين عن طريق اعطاء كل منهم مرجع للأخر و ترجع كائن جديد يحتوي كليهما.

البناء الناتج في الذاكرة:

حتي الأن, كل الكائنات يمكن الوصول اليها. و الأن فلنزيل اثنين من المراجع:

delete family.father;
delete family.mother.husband;

الغاء مرجع واحد فقط من المرجعين لا يكفي ,ستظل امكانية الوصول الي الكائنين ممكنة.

و لكن اذا تم الغاء المرجعين, هنا يمكن ان نري ان جون لم يعد له اي مرجع:

Outgoing references do not matter. Only incoming ones can make an object reachable. So, John is now unreachable and will be removed from the memory with all its data that also became unaccessible.

بعد عملية جمع القمامة:

الجزيرة التي لا يمكن الوصول اليها

هنالك امكانية ان تصبح جزيرة بأكملها من الكائنات المترابطة لا يمكن الوصول اليها و ان تمحي من الذاكرة.

الكائن هو كما بالمثال السابق, و بالتالي:

family = null;

الصورة من داخل الذاكرة تصبح كالتالي:

هذا المثال يشرح اهمية مفهوم قابلية الوصول

من الواضح ان جون و آن ما زالا متصلين ببعضهما بمراجع, لكن هذا غير كافي

كائن "family" السابق فقد اتصاله بالجذر, لا يوجد له اي مرجع الآن, لذا فالجزيرة باكملها لا يمكن الوصول اليها و تتم ازالتها.

الخوارزميات الداخلية

The basic garbage collection algorithm is called “mark-and-sweep”. الخوارزمية الأساسية لجمع القمامة تسمي ب "mark-and-sweep".

تحدث عملية جمع القمامة غالبا علي عدة خطوات:

  • جامع القمامة يأخذ الجذر و يضع عليه علامة
  • ثم يزور كل المراجع المرتبطة به و يضع علي كل منهم علامة
  • ثم يزور كل كل الكائنات التي عليها علامة و يضع علامة علي كل مراجعهم, كل الكائنات التي تمت زيارتها يتم تذكرها حتي لا تتم زيارة نفس الكائن مرتين.
  • … و هكذا الي ان يتم وضع علامة علي كل المراجع بداية من الجذر
  • كل الكائنات عدي التي تم وضع علامة عليها يتم ازالتها

كمثال, فلنفترض ان بناء الكائن لدينا كالتالي:

يمكننا بكل بساطة ملاحظة جزيرة لا يمكن الوصول اليها في الجانب الأيمن. و الآن نستطيع ان نري كيف يطبق جامع القمامة ال "mark-and-sweep".

أول خطوة هي وضع علامة عل الجذر:

ثم وضع علامة علي كل المراجع المرتبطة به:

…و مراجعم كذلك ان امكن

و الآن, كل الكائنات التي لم تتم زيارتها في هذه العملية تعتبر كائنات لا يمكن الوصول اليها و ستتم ازالتها:

يمكننا تخيل العملية كصب دلو كبير من الطلاء من الجذر و الذي سيسري خلال كل المراجع و يضع علامة علي كل الكائنات التي يمكن الوصول اليها, و الكائنات التي لا يمكن الوصول اليها يتم ازالتها.

تلك هي المبادئ التي يعمل علي اساسها جامع القمامة. محرك ال JavaScript يطبق العديد من التحسينات لجعله يعمل بشكل اسرع و الا يؤثر علي الآداء.

بعض هذه التحسينات:

  • مجموعة الأجيال – -- يتم تقسيم الكائنات الي مجموعتين “الجديدة”, “القديمة”, الكثير من الكائنات يتم انشائها و تؤدي وظيفتها و تموت بسرعة, فيمكن التخلص منها بسرعة. اما الكائنات التي تعيش لفترة طوية تصبح “قديمة” و لا يتم التحقق منها و ازالتها بنفس الكثافة.

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

  • مجموعة وقت الخمول – يحاول جامع القمامة ان يعمل في حالة ان وحدة المعالجة المركزية (CPU) في حالة خمول حتي لا يؤثر علي عملية التنفيذ.

هنالك العديد من التحسينات في خوارزميات جامع القمامة. و علي قدر ما اود ان اشرحها هنا,يجب ان نتوقف و ذلك لأن المحركات الختلفة تتبني طرق و حلول مختلفة و الأهم من ذلك ان الأشياء تتغير بتتطور المحركات, لذا ادرس أكثر “مقدما” فبدون الحاجة الحقيقية لمعرفتها فهي لا تستحق العناء الا ان كنت و بالطبع تمتلك الشغف للمعرفة فالروابط بالأسفل ستساعدك بالتأكيد.

الملخص

اهم النقاط لتعرفها:

  • جامع القمامة يعمل بشكل تلقائي, لا يمكن اجباره علي العمل او ايقافه
  • الكائنات تظل في الذاكرة طالما كان بالمكان الوصول اليها
  • كون الكائن له مرجع لا يعني بالضرورة ان يمكن الوصول اليه(من الجذر): قد تصبح مجموعة من الكائنات لا يمكن الوصول اليها

المحركات الحديثة تطور خوارزميات حديثة لعملية جمع القمامة

كتاب "The Garbage Collection Handbook: The Art of Automatic Memory Management"(R.Jones et al) يجمع بعضها

اذا كنت علي علم بالمستويات العميقة من البرمجيات , فهنالك المزيد من المعلومات عن جـامع القمامة V8 في هذا المقال

A tour of V8: Garbage Collection

تنشر ايضا V8 blog مقالات حول تنظيم الذاكرة من آن الي أخر, بطبيعة الحال, لتعلم جامع القمامة يفضل ان تتهيأ عن طريق تعلم مكونات V8 بشكل عام و قراءة مدونة Vyacheslav Egorov و الذي عمل كأحد مهندسي V8. استطيع ان أقول V8 تحديدا لأنه الأكثر تغطية عن طريق المقالات علي الانترنت. و بالنسبة للمحركات الأخري, العديد من الطرق متشابهة, و لكن جامع القمامة يختلف في نقاط عديدة.

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

خريطة الدورة التعليمية

التعليقات

إقرأ هذا قبل أن تضع تعليقًا…
  • إذا كان لديك اقتراحات أو تريد تحسينًا - من فضلك من فضلك إفتح موضوعًا فى جيتهاب أو شارك بنفسك بدلًا من التعليقات.
  • إذا لم تستطع أن تفهم شيئّا فى المقال - وضّح ماهو.
  • إذا كنت تريد عرض كود استخدم عنصر <code> ، وللكثير من السطور استخدم <pre>، ولأكثر من 10 سطور استخدم (plnkr, JSBin, codepen…)