إدارة الذاكرة وجامع القمامة في JavaScript: كيف يعمل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا ينظف JavaScript تلقائياً من ورائنا، ولكن لا يزال يتعين علينا فهم كيف يعمل؟

\n\n

عندما تكتب بلغة JavaScript، نادراً ما تفكر فيما يحدث للمتغيرات بعد خروجها من نطاق الرؤية. على عكس C أو C++، حيث يتعين عليك يدوياً استدعاء free() أو delete، فإن JavaScript يتحمل عناء العمل القذر لتنظيف الذاكرة. هذا مريح - حتى تأتي اللحظة التي يبدأ فيها تطبيقك في "التباطؤ"، أو استهلاك غيغابايت من RAM، أو الانهيار فجأة مع خطأ Out of Memory.

\n\n

هل هذا مألوف؟ إذن دعنا نفهم كيف تعمل إدارة الذاكرة في JavaScript بالفعل وما هو جامع القمامة في JavaScript. المفسد: هذا ليس سحراً، بل خوارزمية صارمة يمكن (ويجب) أخذها في الاعتبار عند كتابة الكود.

\n\n

كيف يخزن JavaScript البيانات: المكدس والكومة

\n\n

قبل الحديث عن جمع القمامة، يجب أن نفهم أين تعيش البيانات. في JavaScript، هناك مخزنان رئيسيان:

\n\n
    \n
  • المكدس (Stack) - ذاكرة سريعة ولكنها محدودة. يتم تخزين الأنواع البدائية (الأرقام، السلاسل النصية، القيم المنطقية) والمراجع إلى الكائنات هناك. كل استدعاء دالة ينشئ إطاراً جديداً في المكدس.
  • \n
  • الكومة (Heap) - منطقة ذاكرة غير منظمة، حيث تعيش الكائنات والمصفوفات والدوال والإغلاقات. هنا يحدث الصراع الرئيسي على الذاكرة.
  • \n
\n\n

عندما تنشئ كائناً:

\n\n
let user = { name: 'إيفان', age: 30 };\n
\n\n

المرجع user يذهب إلى المكدس، بينما الكائن نفسه { name: 'إيفان', age: 30 } يذهب إلى الكومة. طالما أن هناك مرجعاً واحداً على الأقل للكائن، فإنه يبقى حياً. بمجرد أن لا يتبقى أي مراجع، يصبح الكائن "قمامة".

\n\n

جامع القمامة في JavaScript: البطل الرئيسي

\n\n

جامع القمامة في JavaScript (Garbage Collector, GC) هو عملية خلفية تفحص الكومة بشكل دوري وتزيل الكائنات غير القابلة للوصول. يبدو الأمر بسيطاً، لكنه في الواقع حل وسط معقد بين سرعة التنظيف وأداء الكود الخاص بك.

\n\n

خوارزمية Mark-and-Sweep (الوسم والكنس)

\n\n

تستخدم المحركات الحديثة (V8 في Chrome، SpiderMonkey في Firefox) خوارزمية Mark-and-Sweep. تعمل على مرحلتين:

\n\n
    \n
  1. Mark (الوسم): يبدأ GC من الكائنات الجذرية (الكائن العام، الدوال الحالية، المتغيرات المحلية). يجتاز جميع المراجع من الجذور ويوسم كل كائن قابل للوصول.
  2. \n
  3. Sweep (الكنس): جميع الكائنات غير الموسومة تعتبر قمامة ويتم إزالتها. يتم تحرير الذاكرة.
  4. \n
\n\n

مثال توضيحي:

\n\n
let a = { data: 'A' };\nlet b = { data: 'B' };\na.ref = b;  // a يشير إلى b\nb.ref = a;  // b يشير إلى a\n\n// الآن لدينا مرجع دائري\na = null;   // a لم يعد يشير إلى الكائن\nb = null;   // b لم يعد يشير إلى الكائن\n
\n\n

في السابق (قبل ظهور Mark-and-Sweep) كانت هذه المراجع الدائرية تؤدي إلى تسربات. لكن GC الحديث يفهم: إذا كان كلا الكائنين غير قابلين للوصول من الجذر، فهما قمامة، حتى لو كانا يشيران إلى بعضهما البعض.

\n\n

أجيال الكائنات: الصغيرة والكبيرة

\n\n

معظم الكائنات تعيش لفترة قصيرة. على سبيل المثال، المتغيرات المؤقتة داخل الدوال. لذلك يقسم V8 الكومة إلى أجيال:

\n\n
    \n
  • Young Generation (الجيل الصغير): الكائنات الجديدة. يتم الجمع هنا بشكل متكرر وسريع (Scavenge - خوارزمية تنسخ الكائنات الحية إلى منطقة أخرى).
  • \n
  • Old Generation (الجيل الكبير): الكائنات التي نجت من عدة عمليات جمع. يتم فحصها بشكل أقل، لكن العملية تستغرق وقتاً أطول.
  • \n
\n\n

هذا النهج يسمح بعدم "تجميد" الصفحة أثناء الفحص الكامل للكومة بأكملها.

\n\n

تسربات الذاكرة في JavaScript: كيف تسببها (ولا تفعل)

\n\n

حتى مع جامع القمامة الذكي، يمكنك خلق موقف لا يتم فيه تحرير الذاكرة. إليك الأسباب الأكثر شيوعاً لـ تسرب

المدونات

توصيات الكتب