جمع المهملات هي ميزة أساسية في لغة برمجة Java التي تدير تلقائيًا تخصيص الذاكرة وإلغاء التخصيص للكائنات التي يتم إنشاؤها في مساحة eden.
تسمح عملية جمع المهملات في لغة Java للمطورين بالتركيز على كتابة التعليمات البرمجية دون القلق بشأن إدارة الذاكرة، ما يجعل لغة Java خيارًا شائعًا لبناء تطبيقات معقدة وواسعة النطاق. ومع ذلك، فإن فهم آلية عملية جمع المهملات أمر ضروري لمطوري Java لتحسين أداء التعليمات البرمجية الخاصة بهم وتجنب الأخطاء الشائعة المتعلقة بالذاكرة.
في هذا الدليل، يتم استكشاف أساسيات تجميع المهملات في Java، بما في ذلك فوائدها وأنواع مختلفة من المجمّعات وأفضل الممارسات التي يجب اتباعها أثناء البرمجة. لذا، لنتعمق أكثر ونستكشف كيفية عمل تجميع المهملات!
OutofMemoryError هو نوع من الأخطاء التي تحدث عندما يحاول برنامج أو تطبيق تخصيص ذاكرة أكثر من الكمية المتوفرة. يحدث هذا الخطأ عندما تنفد ذاكرة Java Virtual Machine أو منصة آخرى أثناء محاولة تشغيل تطبيق.
يحدث خطأ OutofMemoryError عادةً عندما يحاول تطبيق أو برنامج إنشاء كائنات جديدة، لكن JVM غير قادر على تخصيص ذاكرة لاستيعابها. يمكن أن يحدث هذا الخطأ أيضاً عندما يستخدم التطبيق الكثير من الذاكرة ولا يقوم بتحريرها بشكل صحيح.
عند حدوث خطأ نفاد الذاكرة OutofMemoryError، عادةً ما يتعطل التطبيق وينتهي. هذا الخطأ شائع في البرامج التي تتعامل مع كميات كبيرة من البيانات الوصفية، مثل تطبيقات معالجة الصور أو الفيديو، أو البرامج التي تتعامل مع قواعد بيانات كبيرة.
لحل هذا الخطأ، قد تحتاج إلى زيادة مقدار الذاكرة المتوفرة للتطبيق أو تحسين استخدام ذاكرة التطبيق. يمكن القيام بذلك عن طريق تعديل معلمات JVM أو باستخدام أداة تحليل الذاكرة لتحديد تسرب الذاكرة أو الاستخدام غير الفعال للذاكرة.
في Java، يتم تخزين جميع الكائنات على الكومة، وهي جزء من الذاكرة محجوز للتخصيص الديناميكي للكائنات. عندما لا تتم الإشارة إلى كائن ما بواسطة أي جزء من البرنامج، يصبح مؤهلاً لجمع المهملات.
يقوم جامع المهملات في Java بفحص ذاكرة الكومة بشكل دوري للعثور على الكائنات التي لا يتم استخدامها. تتضمن عملية جمع المهملات عدة خطوات، بما في ذلك التحديد والإزالة والتجميع.
وضع العلامات - تتضمن الخطوة الأولى لجمع المهملات وضع علامة على جميع الكائنات التي لا يزال البرنامج يشير إليها. يتم ذلك من خلال البدء بمجموعة من الكائنات الجذرية، مثل المتغيرات العامة والمتغيرات المحلية ومعلمات الأسلوب، ثم تتبع جميع الكائنات التي يمكن الوصول إليها من تلك الجذور. تعتبر الكائنات التي لا يمكن الوصول إليها من الجذور مؤهلة لجمع المهملات.
المسح - بعد مرحلة وضع العلامات، يقوم مُجمّع القمامة بمسح كومة Java لتحديد واستعادة الذاكرة المستخدمة من قبل الكائنات التي لم تعد تتم الإشارة إليها. يتضمن ذلك تحديد موقع الذاكرة التي تستخدمها الكائنات غير المستخدمة وإضافتها مرة أخرى إلى تجمع الذاكرة الحرة.
الدمج - في بعض خوارزميات جمع المهملات، يتبع مرحلة المسح مرحلة ضغط، حيث يتم إعادة ترتيب الذاكرة المستخدمة من قبل الكائنات المتبقية لتقليل التجزئة. يتضمن ذلك تحريك الكائنات بالقرب من بعضها البعض وإنشاء كتل متجاورة أكبر من الذاكرة الحرة.
تقوم آلة Java الافتراضية (JVM) تلقائيًا بإجراء جمع المهملات، وبالتالي لا يتعين على المبرمج إدارة الذاكرة يدويًا. يعمل مُجمِّع القمامة على مسار منفصل ويعمل عادةً في الخلفية، لذا فهو لا يؤثر على التنفيذ العادي للبرنامج.
هناك نوعان رئيسيان من خوارزميات جمع المهملات في Java: جمع المهملات الكامل وجمع المهملات التزايدي.
جمع المهملات الكامل هو عملية يقوم فيها مُجمّع القمامة (جزء من نظام تشغيل لغة البرمجة) بالبحث في كل الذاكرة المستخدمة من قِبل البرنامج وتجميع أي كائنات لم تعد مستخدمة بواسطة البرنامج. ثم يتم وضع علامة على هذه الكائنات كمهملات وتكون مؤهلة للإزالة من الذاكرة.
عادةً ما يتم تنفيذ عملية التجميع الكامل للقمامة بواسطة نظام وقت التشغيل للغة برمجة تستخدم إدارة الذاكرة التلقائية، مثل Java أو Python. أثناء العملية، يقوم مجمع المهملات بإيقاف تنفيذ البرنامج مؤقتًا لإجراء البحث عن كائنات المهملات، مما قد يؤدي إلى تباطؤ مؤقت في أداء البرنامج.
عادة ما يتم تشغيل تجميع المهملات الكامل عندما يصل مقدار الذاكرة المستخدمة من قِبل أحد البرامج إلى حد معين أو عندما يطلب البرنامج كتلة جديدة من الذاكرة، ولا تتوفر ذاكرة خالية كافية. الهدف من جمع المهملات الكامل هو استعادة الذاكرة التي لا يحتاجها البرنامج، مما يجعلها متاحة للاستخدام من قِبل أجزاء أخرى من البرنامج أو بواسطة برامج أخرى تعمل على نفس الجهاز.
الجمع التدريجي للنفايات هو تقنية لإدارة الذاكرة تعتمد على استرجاع الذاكرة غير المستخدمة بشكل تدريجي. تُستخدَم هذه التقنية في بعض لغات البرمجة وبيئات التشغيل لتوفير استرداد تلقائي للذاكرة التي لم يَعُد البرنامج بحاجة إليها. يقوم الجمع التدريجي للنفايات بتحديد الكائنات الموجودة في الذاكرة والتي لم تَعُد قيد الاستخدام، ثم يقوم بتحرير المساحة التي تشغلها هذه الكائنات بحيث تصبح متاحة لإعادة استخدامها من قِبَل أجزاء أخرى من البرنامج.
في الجمع التدريجي للنفايات، يقوم جامع المهملات بمسح ذاكرة البرنامج بشكل دوري للبحث عن الكائنات التي لا يمكن الوصول إليها (غير المستخدمة) في ذاكرة الكومة الخاصة بالجيل الجديد. بدلًا من إيقاف تنفيذ البرنامج خلال عملية المسح، يقوم جامع المهملات بتقسيم عملية المسح إلى أجزاء صغيرة وقابلة للإدارة تُسمَّى "الزيادات". خلال كل زيادة، يفحص جامع المهملات جزءًا من ذاكرة البرنامج، ويحدِّد أي كائنات غير مطلوبة ويضع علامة عليها ككائنات متاحة لإعادة استخدامها.
باستخدام الزيادات، يمكن لمجمّع المهملات استعادة الذاكرة في أجزاء صغيرة، دون مقاطعة تنفيذ البرنامج لفترة طويلة. يساعد ذلك على ضمان بقاء البرنامج مستجيبًا وعدم تعرضه لإيقاف مؤقت أو تأخير كبير نتيجة لعملية جمع المهملات.
ومع ذلك ، يمكن أن يكون جمع المهملات التزايدي أقل كفاءة من الأنواع الأخرى من تقنيات جمع المهملات، مثل وضع العلامات والمسح أو جمع المهملات عبر الأجيال، لأنه يتطلب عمليات مسح أكثر تكرارًا لذاكرة البرنامج. بالإضافة إلى ذلك، يمكن أن يؤدي استخدام الزيادات إلى إدخال بعض النفقات الزائدة في تنفيذ البرنامج، حيث يحتاج مُجمّع المهملات إلى الحفاظ على معلومات الحالة بين كل زيادة.
بشكل عام، يوفر جمع المهملات في Java العديد من الفوائد التي تجعلها أداة قيمة للمطورين. فيما يلي بعض فوائد استخدام جمع المهملات في Java:
لا حاجة لإدارة الذاكرة يدويًا: مع وجود آلية جمع المهملات، لا يُطلب من المطورين إدارة تخصيص الذاكرة وتحريرها يدويًا. وهذا يعني أن المبرمجين يمكنهم التركيز بشكل أكبر على كتابة التعليمات البرمجية بدلًا من إدارة الذاكرة، ما يقلِّل من الأخطاء ويُحسِّن الإنتاجية.
يمنع تسرب الذاكرة: يساعد تجميع المهملات في منع تسرب الذاكرة، والذي يمكن أن يحدث عندما لا يقوم البرنامج بتحرير الذاكرة التي لم تعد هناك حاجة إليها. يمكن أن يتسبب هذا في استهلاك البرنامج لذاكرة أكثر من اللازم، مما يؤدي إلى بطء الأداء، وتعطله في النهاية.
تخصيص الذاكرة الديناميكي: يتيح جمع القمامة في Java تخصيص الذاكرة الديناميكي، مما يعني تخصيص الذاكرة حسب الحاجة في وقت التشغيل. يساعد هذا على منع أخطاء تخصيص الذاكرة ويمكن أن يجعل البرنامج أكثر كفاءة.
أداء أفضل: يمكن أن تساعد الإدارة التلقائية للذاكرة على تحسين أداء البرنامج من خلال تقليل الوقت المستغرق في إدارة الذاكرة. ويمكن أن يؤدي ذلك إلى أوقات تنفيذ أسرع وبرنامج أكثر استجابة.
تحسين الذاكرة: يمكن لعملية جمع المهملات أن تُحسِّن استخدام الذاكرة عبر إعادة استخدام الذاكرة غير المستَغلة من قِبَل جزء من البرنامج لصالح أجزاء أخرى. ويمكن أن يساعد ذلك على تقليل استخدام الذاكرة وتحسين كفاءة البرنامج بشكل عام.
يستفيد المطورون من آلية جمع المهملات في Java لما توفِّره من إدارة تلقائية للذاكرة، ومنع تسرب الذاكرة، وتمكين التخصيص الديناميكي للذاكرة، وتحسين الأداء، وتعزيز كفاءة استخدام الذاكرة، وهذا بدوره يساعدهم على كتابة برامج أفضل وأكثر كفاءة.
في Java، يتم تشغيل عملية جمع المهملات تلقائيًا بواسطة آلة Java الافتراضية (JVM) عندما تحدِّد أن الكومة قد امتلأت أو عندما يمر مقدار معين من الوقت.
هناك العديد من الأحداث التي يمكن أن تؤدي إلى جمع المهملات في Java:
تخصيص مساحة الكومة: عندما يحتاج JVM إلى تخصيص ذاكرة لكائن جديد، ولا توجد مساحة كافية في الكومة، فإنه يقوم بتشغيل عملية جمع المهملات لاستعادة الذاكرة غير المستخدمة أو تخزينها في المساحة الناجية.
استدعاء أسلوب System.gc(): يمكنك طلب جمع المهملات بشكل صريح عن طريق استدعاء طريقة System.gc() ،على الرغم من عدم وجود ضمان لتشغيلها.
عتبة الجيل القديم: يمكن أيضًا تشغيل عملية جمع المهملات عندما يصل حجم كومة مساحة كومة الجيل القديم (التي تخزِّن الكائنات طويلة العمر) إلى عتبة معينة.
عتبة PermGen/Metaspace: في إصدارات جافا قبل جافا 8، يمكن أيضًا تشغيل عملية جمع المهملات عندما يصل حجم مناطق الذاكرة PermGen (التوليد الدائم) أو Metaspace (في جافا 8 والإصدارات الأحدث) إلى عتبة معينة.
على أساس الوقت: في بعض الأحيان، يمكن تشغيل تجميع المهملات بناءً على فاصل زمني. على سبيل المثال، قد يقوم JVM بتشغيل جمع المهملات كل ساعة أو كل يوم، بغض النظر عن استخدام الذاكرة.
تجدر الإشارة إلى أن السلوك الدقيق لجمع المهملات في Java يمكن أن يختلف اعتمادًا على تنفيذ JVM وتكوينه.
لطلب جهاز Java الافتراضي (JVM) لتشغيل أداة جمع المهملات، يمكنك اتباع الخطوات التالية:
استدعاء طريقة System.gc(): يُستخدم هذا الأسلوب لطلب JVM لتشغيل مجمّع القمامة. ليس من المضمون أن يعمل مُجمِّع القمامة فور استدعاء هذه الطريقة.
استخدم علامة -XX:+DisableExplicitGC JVM: تعمل هذه العلامة على تعطيل طلبات جمع المهملات الصريحة. وهذا يعني أنه حتى لو قمت باستدعاء System.gc() أو Runtime.getRuntime().gc()، فلن يتم تشغيل أداة تجميع المهملات.
من المهم التنويه إلى أن طلب تشغيل جامع المهملات بشكل صريح ليس مستحسنًا عادةً، لأن آلة Java الافتراضية (JVM) مصممة لإدارة تخصيص الذاكرة وجمع المهملات تلقائيًا. ويمكن أن يكون لطلبات جمع المهملات الصريحة في بعض الأحيان تأثير سلبي في الأداء.
يكون الكائن في لغة البرمجة مؤهلاً لجمع المهملات عندما لا تتم الإشارة إليه بواسطة أي جزء من البرنامج. جمع المهملات التلقائي هي عملية يتم تنفيذها بواسطة بيئة وقت تشغيل لغة البرمجة لاستعادة الذاكرة.
في معظم لغات البرمجة الحديثة، يتم تنفيذ عملية جمع المهملات تلقائيًا بواسطة بيئة وقت التشغيل. يمكن أن تختلف الخوارزميات المحددة المستخدمة لجمع المهملات اعتمادًا على لغة البرمجة والتنفيذ، ولكن المبدأ العام هو نفسه: تقوم بيئة وقت التشغيل بمسح الكومة بشكل دوري (جزء الذاكرة المستخدم للكائنات المخصصة ديناميكيًا) لتحديد الكائنات التي لم يعد من الممكن الوصول إليها من أي كائن مستخدم في البرنامج. بمجرد تحديد كائن على أنه غير قابل للوصول إليه، يتم وضع علامة عليه ككائن مهمل ويمكن استعادة ذاكرته.
يعتمد التوقيت الدقيق للوقت الذي يكون فيه كائن ما مؤهلاً لجمع المهملات على خوارزمية جمع المهملات المحددة التي تستخدمها بيئة وقت التشغيل. بعض الخوارزميات أكثر عدوانية من غيرها وقد تستعيد الذاكرة بسرعة أكبر، بينما قد تؤخر خوارزميات أخرى عملية جمع المهملات لتحسين الأداء. ومع ذلك، بشكل عام، لا يحتاج المطور إلى القلق بشأن إدارة الذاكرة يدويًا، حيث تتولى بيئة وقت التشغيل هذا الأمر تلقائيًا.
هناك العديد من أدوات جمع المهملات في جافا، بما في ذلك:
مُجمّع القمامة التسلسلي: المُجمّع التسلسلي هو المُجمّع الافتراضي للمهملات في Java ويستخدم عادةً في التطبيقات الصغيرة والمتوسطة الحجم التي لا تتطلب إنتاجية عالية. يساعد هذا النوع من المجمعات على منع حدوث أحداث "إيقاف العالم (stop the world)" الشائعة.
جامع المهملات المتوازي: صُمِّم جامع المهملات المتوازي للتطبيقات التي تتطلب إنتاجية عالية، وهو مفيد بشكل خاص في التطبيقات التي تحتاج إلى كومات كبيرة؛ لأنه يستخدم وحدات معالجة مركزية متعددة لتسريع عملية جمع المهملات. ومن المهم ملاحظة أن هذا النوع من جامع المهملات يقوم بتجميد سلسلة تعليمات التطبيقات عند تشغيل عملية جمع المهملات.
مُجمِّع المسح المتزامن للعلامات (CMS): تم تصميم مُجمِّع CMS للتطبيقات التي تتطلب أوقات توقف مؤقت منخفضة وهو مفيد في التطبيقات التي تحتوي على العديد من الكائنات المستخدمة.
مُجمِّع المهملات G1: تم تصميم مُجمِّع G1 للأكوام الكبيرة ويمكنه التعامل مع مزيج من الكائنات قصيرة وطويلة العمر. يستخدم خيوطًا متعددة لمسح الكومة وضغطها في نفس الوقت.
كل جامع نفايات له نقاط القوة والضعف الخاصة به، ويعتمد اختيار النوع الذي يجب استخدامه على الاحتياجات المحددة للتطبيق. ومن الممكن أيضًا تكوين إعدادات جامع المهملات وضبطها لتحسين الأداء لتطبيق معين.
يرتبط كلٌّ من جمع القمامة وعمليات تسرُّب الذاكرة بإدارة الذاكرة في البرامج الحاسوبية، ولكن لكلٍّ منهما معانٍ وآثار مختلفة.
كما ذُكر سابقًا، يتم عادةً إجراء جمع المهملات بواسطة لغة البرمجة أو بيئة وقت التشغيل، وهذا يساعد على ضمان عدم استهلاك البرامج لذاكرة أكثر مما تحتاج إليه. تُحدِّد عملية جمع المهملات الذاكرة التي يمكن استخدامها من قِبَل أجزاء أخرى من البرنامج أو من قِبَل برامج أخرى تعمل على جهاز الكمبيوتر.
من ناحية أخرى، يحدث تسرُّب في الذاكرة عندما يفشل البرنامج في تحرير الذاكرة التي قام بتخصيصها، حتى عندما لم تَعُد تلك الذاكرة مطلوبة. نتيجةً لذلك، يستمر البرنامج في استهلاك الذاكرة مع مرور الوقت، ما يؤدي في النهاية إلى استنفاد الذاكرة المتاحة، ويمكن أن يتسبَّب هذا في تعطل البرنامج أو نظام التشغيل بالكامل. عادةً ما تحدث تسريبات الذاكرة بسبب أخطاء في البرنامج، وقد يكون من الصعب تحديدها وإصلاحها.
باختصار، تجميع المهملات هي عملية لتحرير الذاكرة التي لم تعد هناك حاجة إليها تلقائيًا. يحدث تسرب الذاكرة عندما يتم تخصيص الذاكرة ولكن لا يتم تحريرها من بواسطة أحد البرامج، مما يتسبب في تراكم تدريجي لاستخدام الذاكرة.
في الختام، يعتبر جمع القمامة جانبًا أساسيًا في برمجة Java يضمن إدارة الذاكرة بكفاءة من خلال استعادة الذاكرة غير المستخدمة. توفر قابلية الملاحظة في Instana للمطورين أدوات قوية لمراقبة عملية جمع القمامة وتحسينها في الوقت الفعلي.
باستخدام Instana، يمكن للمطورين تحديد تسريبات الذاكرة بسرعة، وتحسين إعدادات تجميع المهملات، واستكشاف مشكلات الأداء المتعلقة بتجميع المهملات وإصلاحها.
وباستخدام قدرات المراقبة الشاملة من Instana، يمكن للمطورين اكتساب رؤى متعمقة حول استخدام ذاكرة تطبيقات Java الخاصة بهم وسلوك جمع المهملات، ما يمكِّنهم من تقديم برامج عالية الأداء وموثوق بها.
من خلال اتِّباع أفضل الممارسات الموضحة في هذا الدليل، يمكن للمطورين استخدام Instana لتحسين عملية جمع المهملات وتحسين الأداء العام لتطبيقاتهم المكتوبة بلغة Java. ومع ميزة قابلية الملاحظة في Instana، يمكن للمطورين البقاء على اطِّلاع مسبَق بأي مشكلات قد تنشأ، ما يضمن استمرار تشغيل تطبيقاتهم بكفاءة عالية.
يعزز الوظائف وقابلية الملاحظة في مراقبة أداء التطبيقات في مؤسستك؛ ويحسِّن إدارة أداء التطبيقات ويسرّع مسارات التكامل المستمر والتسليم المستمر (CI/CD) بغض النظر عن مكان وجود التطبيقات.