دورة حياة الكائن وجمع البيانات غير المستخدمة
دورة حياة الكائن وجمع البيانات غير المستخدمة
كل كائن في Java يُولد، ويعيش في الذاكرة لفترة ما، ثم يموت في نهاية المطاف. خلافًا للغات مثل C و C++، لا تضطر أبدًا إلى تحرير الذاكرة يدويًا في Java — فـمجمّع البيانات غير المستخدمة (Garbage Collector — GC) يتولّى ذلك تلقائيًا. فهم متى ولماذا يُزال كائن ما يساعدك على كتابة كود صحيح وكفؤ في استخدام الذاكرة.
الخطوة الأولى — إنشاء الكائن
يُنشأ الكائن باستخدام الكلمة المحجوزة new. تقوم Java بشيئين في تلك اللحظة: تخصّص قطعة من الذاكرة في الكومة (Heap) لاستيعاب حقول الكائن، ثم تُشغّل الباني (Constructor) لتهيئة تلك الحقول.
بعد هذا السطر، rex هو متغير مرجعي (Reference Variable). لا يحتوي على الكائن نفسه، بل يحتوي على عنوان الذاكرة الذي يقطنه الكائن. فكّر فيه كجهاز تحكّم عن بُعد: جهاز التحكّم هو المتغير، والتلفاز هو الكائن في الكومة.
الخطوة الثانية — المراجع تُبقي الكائنات حيّة
يبقى الكائن حيًا طالما يوجد مرجع واحد على الأقل يشير إليه. يمكن أن يكون لديك مراجع متعددة للكائن نفسه:
الخطوة الثالثة — الأهلية للجمع (GC)
يصبح الكائن مؤهلًا لجمع البيانات غير المستخدمة في اللحظة التي لا يشير إليه فيها أي مرجع يمكن الوصول إليه. هناك عدة طرق تحدث بها هذه الحالة:
- خروج المتغير من النطاق (Scope). تُدمَّر المتغيرات المحلية عند انتهاء الدالة التي تنتمي إليها.
- إعادة إسناد المتغير. يفقد الكائن القديم مرجعه.
- ضبط المتغير على
null. يُزيل المرجع صراحةً.
null حين تنتهي منها إذا كانت مخزّنة في متغيرات طويلة العمر (مثل الحقول الساكنة static أو حقول كائن طويل الأمد). أما المتغيرات المحلية فلا تحتاج لذلك لأنها تختفي تلقائيًا عند انتهاء الدالة.
الخطوة الرابعة — تشغيل مجمّع البيانات غير المستخدمة
كون الكائن مؤهلًا للجمع لا يعني تحرير ذاكرته فورًا. يعمل مجمّع البيانات غير المستخدمة التابع للـ JVM وفق جدولته الخاصة — عادةً حين تشحّ ذاكرة الكومة. يتتبّع جميع الكائنات التي يمكن الوصول إليها انطلاقًا من مجموعة مراجع جذرية (Root References) (المتغيرات المحلية في الخيوط الجارية، والحقول الساكنة) ويستردّ كل ما لا يمكن الوصول إليه.
يمكنك اقتراح تشغيل GC عبر System.gc()، لكن الـ JVM حرّ في تجاهل هذه الإشارة. لا تعتمد عليها أبدًا.
try-with-resources.
جزيرة العزل (Island of Isolation)
يمكن للكائنات أن تشير إلى بعضها وتبقى غير قابلة للوصول من الكود الخارجي في آنٍ واحد. يُسمى هذا جزيرة العزل. يتعامل الـ GC الخاص بـ JVM مع هذا بشكل صحيح لأنه يعتمد على إمكانية الوصول من المراجع الجذرية، لا على عدّ المراجع فحسب.
دورة حياة الكائن — خلاصة
- الإنشاء: تخصّص
newذاكرة في الكومة وتُشغّل الباني. - الاستخدام: مرجع واحد على الأقل قابل للوصول يُبقيه حيًا.
- الأهلية للجمع: لا يوجد أي مرجع قابل للوصول.
- الاسترداد: يُحرّر GC الذاكرة في الوقت الذي يراه مناسبًا.
free() يدوي في Java. صُمِّمت Java لكي يركّز المطوّرون على منطق الأعمال لا على إدارة الذاكرة. مجمّع البيانات متطوّر للغاية — المجمّعات الحديثة (G1، ZGC، Shenandoah) قادرة على استرداد الذاكرة بفترات توقف قصيرة جدًا حتى في التطبيقات الكبيرة.
الخلاصة العملية
في معظم كود Java اليومي لا تحتاج للتفكير في GC على الإطلاق. فقط دع الكائنات تخرج من نطاقها بصورة طبيعية. الحالات التي يهمّ فيها الأمر هي: الكائنات الكبيرة المحفوظة في متغيرات طويلة العمر، والمجموعات المخزّنة مؤقتًا التي تنمو بلا حدود، والصفوف التي تغلّف موارد أصيلة. في تلك الحالات، حرّر المراجع صراحةً واستخدم try-with-resources لكل ما يمكن إغلاقه.