يدير JVM نوعين من الذاكرة، الكومة وغير الكومة. الكومة مخصصة للمطورين، كما هو مذكور أعلاه، يتم إنشاؤها عند بدء تشغيل JVM؛ أما الكومة غير المحجوزة لـ JVM نفسها لتخزين معلومات الفئة. وهو يختلف عن الكومة. لن يقوم GC بتحرير مساحة أثناء وقت التشغيل.
1. نوع تجاوز الذاكرة
1.java.lang.OutOfMemoryError: مساحة PermGen
يدير JVM نوعين من الذاكرة، الكومة وغير الكومة. الكومة مخصصة للمطورين، كما هو مذكور أعلاه، يتم إنشاؤها عند بدء تشغيل JVM؛ أما الكومة غير المحجوزة لـ JVM نفسها لتخزين معلومات الفئة. وهو يختلف عن الكومة. لن يقوم GC بتحرير مساحة أثناء وقت التشغيل. إذا كان تطبيق الويب يستخدم عددًا كبيرًا من جرة الطرف الثالث أو كان التطبيق يحتوي على عدد كبير جدًا من ملفات الفئة وكان إعداد MaxPermSize صغيرًا، فإن تجاوز الحد سيؤدي أيضًا إلى شغل الذاكرة كثيرًا ويتسبب في تجاوز السعة، أو سوف يقوم القط لن يتم تنظيف المقدمة أثناء النشر الساخن، ولن تغير البيئة المحملة إلا السياق إلى البيئة المنشورة حديثًا، وسيكون هناك المزيد والمزيد من المحتوى غير المكدس.
الاسم الكامل لمساحة PermGen هو مساحة الجيل الدائم، والتي تشير إلى منطقة التخزين الدائمة في الذاكرة. يتم استخدام هذه الذاكرة بشكل أساسي بواسطة JVM لتخزين معلومات الفئة والوصف يتم تحميله بواسطة Loader، ويقوم بتخزين مثيلات الفئة (المثيل) ومنطقة الكومة مختلفة، ولن يقوم GC (مجموعة البيانات المهملة) بتنظيف مساحة PermGen أثناء تشغيل البرنامج الرئيسي، لذلك إذا كان تطبيقك يحتوي على الكثير من CLASS، فإن PermGen من المرجح أن تظهر. خطأ في المساحة، هذا الخطأ شائع عندما يقوم خادم الويب بترجمة JSP مسبقًا. إذا كان تطبيق WEB الخاص بك يستخدم عددًا كبيرًا من الجرار التابعة لجهات خارجية، ويتجاوز حجمها الحجم الافتراضي لـ jvm (4M)، فسيتم إنشاء رسالة الخطأ هذه.
أفضل مثال على التكوين: (بعد التحقق بنفسي، منذ استخدام هذا التكوين، لم يمت Tomcat مرة أخرى أبدًا)
اضبط JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
ضمن Linux، أضف سطرًا من التعليمات البرمجية كما هو موضح باللون الأحمر في tomcathome/conf/catalina.sh: يمكنك زيادة ذاكرة tomcat jvm، بحيث تقل احتمالية حدوث تجاوز في الذاكرة!
# ----- تنفيذ الأمر المطلوب ---------------------------------------- -
JAVA_OPTS="-server -Xms512m -Xmx2048m -XX:PermSize=128m -XX:MaxNewSize=256m -XX:MaxPermSize=256m"
# Bugzilla 37848: قم بإخراج هذا فقط إذا كان لدينا جهاز TTY
2.java.lang.OutOfMemoryError: مساحة Javaheap
الحالة الأولى هي تكملة، والمشكلة الرئيسية تحدث في هذه الحالة. مساحتها الافتراضية (أي -Xms) هي 1/64 من الذاكرة الفعلية، والحد الأقصى للمساحة (-Xmx) هي 1/4 من الذاكرة الفعلية. إذا بقي أقل من 40% من الذاكرة، فسيقوم JVM بزيادة الكومة إلى القيمة التي حددها Xmx. إذا تجاوزت الذاكرة المتبقية 70%، فسيقوم JVM بتقليل الكومة إلى القيمة التي حددها Xms. لذلك، يجب بشكل عام تعيين إعدادات Xmx وXms للخادم بنفس الطريقة لتجنب ضبط حجم كومة الجهاز الظاهري بعد كل GC. بافتراض أن الذاكرة الفعلية لا نهائية، فإن الحد الأقصى لذاكرة JVM يعتمد على نظام التشغيل. بشكل عام، يتراوح حجم الجهاز 32 بت بين 1.5 جرام و3 جرام، في حين أن الجهاز 64 بت ليس له حد.
ملاحظة: إذا تجاوز Xms قيمة Xmx، أو تجاوز مجموع الحد الأقصى لقيمة الكومة والحد الأقصى لقيمة الكومة غير الحد الأقصى للذاكرة الفعلية أو نظام التشغيل، فلن يبدأ الخادم.
دور جمع القمامة GC
لا يزال تكرار استدعاء JVM لـ GC مرتفعًا جدًا، ويتم تنفيذ جمع البيانات المهملة في حالتين رئيسيتين:
عندما يكون مؤشر ترابط التطبيق خاملاً؛ والآخر هو عندما تكون كومة ذاكرة Java غير كافية، فسيتم استدعاء GC بشكل مستمر. إذا لم تتمكن إعادة التدوير المستمرة من حل مشكلة عدم كفاية كومة الذاكرة، فسيتم الإبلاغ عن خطأ نفاد الذاكرة. ونظرًا لأن هذا الاستثناء يعتمد على بيئة تشغيل النظام، فمن المستحيل التنبؤ بموعد حدوثه.
وفقًا لآلية GC، سيؤدي تشغيل البرنامج إلى حدوث تغييرات في بيئة تشغيل النظام، مما يزيد من فرصة تشغيل GC.
لتجنب هذه المشاكل، يجب أن يتجنب تصميم البرامج وكتابتها إشغال الذاكرة للكائنات المهملة والحمل الزائد لـ GC. يمكن أن يشير استدعاء System.GC() بشكل صريح فقط إلى أن JVM يحتاج إلى إعادة تدوير الكائنات المهملة في الذاكرة، ولكن ليس من الضروري إعادة تدويرها على الفور.
الأول هو أنه لا يمكنه حل مشكلة نفاد موارد الذاكرة، كما أنه سيزيد من استهلاك GC.
2. تكوين منطقة ذاكرة JVM <BR> تحدث ببساطة عن الكومة والمكدس في Java
تقسم Java الذاكرة إلى نوعين: أحدهما ذاكرة مكدسة والآخر ذاكرة كومة.
1. يتم تخصيص متغيرات النوع الأساسي والمتغيرات المرجعية للكائنات المحددة في الوظيفة في ذاكرة المكدس الخاصة بالوظيفة؛
2. تُستخدم ذاكرة الكومة لتخزين الكائنات والمصفوفات التي تم إنشاؤها بواسطة جديد. عندما يتم تعريف متغير في دالة (كتلة التعليمات البرمجية)، تقوم Java بتخصيص مساحة الذاكرة للمتغير على المكدس قم بتحريرها تلقائيًا وإزالة مساحة الذاكرة المخصصة للمتغير؛ تتم إدارة الذاكرة المخصصة في الكومة بواسطة أداة تجميع البيانات المهملة التلقائية لجهاز Java الظاهري لا يحتاج إلى إخبار المترجم مسبقًا، لأنه في الذاكرة يتم تخصيصه ديناميكيًا في وقت التشغيل. العيب هو أنه يجب تخصيص الذاكرة ديناميكيًا في وقت التشغيل، وتكون سرعة الوصول بطيئة؛
تتمثل ميزة المكدس في أن سرعة الوصول أسرع من سرعة الكومة. العيب هو أن حجم وعمر البيانات المخزنة في المكدس يجب أن يكون محددًا وغير مرن.
تنقسم كومة جافا إلى ثلاث مناطق: جديدة وقديمة ودائمة
يحتوي GC على موضوعين:
يتم تخصيص الكائنات التي تم إنشاؤها حديثًا إلى المنطقة الجديدة. عند ملء المنطقة، سيتم نقلها إلى المنطقة القديمة بواسطة مؤشر ترابط GC المساعد. عندما يتم ملء المنطقة القديمة أيضًا، سيتم تشغيل مؤشر ترابط GC الرئيسي لاجتياز جميع الكائنات ذاكرة الكومة. حجم المنطقة القديمة يساوي Xmx ناقص -Xmn
تعديل مكدس تخزين مكدس Java: المعلمات هي +UseDefaultStackSize -Xss256K، مما يعني أن كل موضوع يمكن أن ينطبق على مساحة مكدس تبلغ 256 كيلو بايت. كل موضوع له مكدس خاص به.
3. كيفية ضبط الذاكرة الظاهرية في JVM <BR>نصيحة: في JVM، إذا تم استخدام 98% من الوقت لـ GC وكان حجم الكومة المتاح أقل من 2%، فسيتم طرح رسالة الاستثناء هذه.
نصيحة: يجب ألا يتجاوز الحد الأقصى لحجم الكومة 80% من الذاكرة الفعلية المتوفرة. بشكل عام، يجب تعيين خيارات -Xms و-Xmx على نفس القيمة، ويجب أن تكون -Xmn 1/4 من القيمة -Xmx.
نصيحة: يتم تحديد الذاكرة الأولية المخصصة بواسطة JVM بواسطة -Xms، والذاكرة الافتراضية هي 1/64 من الذاكرة الفعلية؛ ويتم تحديد الحد الأقصى للذاكرة المخصصة بواسطة JVM بواسطة -Xmx، والذاكرة الافتراضية هي 1/4 من الذاكرة الفعلية ذاكرة.
بشكل افتراضي، عندما تكون ذاكرة الكومة المجانية أقل من 40%، سيقوم JVM بزيادة الكومة حتى الحد الأقصى -Xmx؛ عندما تكون ذاكرة الكومة المجانية أكبر من 70%، سيقوم JVM بتقليل الكومة إلى الحد الأدنى -Xms. لذلك، يقوم الخادم عمومًا بتعيين -Xms و-Xmx ليكونا متساويين لتجنب ضبط حجم الكومة بعد كل GC.
نصيحة: بافتراض أن الذاكرة الفعلية لا نهائية، فإن القيمة القصوى لذاكرة JVM لها علاقة كبيرة بنظام التشغيل.
بكل بساطة، على الرغم من أن المعالج 32 بت يحتوي على مساحة ذاكرة يمكن التحكم فيها تبلغ 4 جيجابايت، إلا أن نظام التشغيل المحدد سيفرض حدًا.
هذا الحد بشكل عام هو 2 جيجا بايت - 3 جيجا بايت (بشكل عام، هو 1.5 جيجا بايت - 2 جيجا بايت في أنظمة Windows و2 جيجا - 3 جيجا بايت في أنظمة Linux)، ولن يكون هناك حد للمعالجات التي تزيد عن 64 بت.
ملاحظة: إذا تجاوز Xms قيمة Xmx، أو تجاوز مجموع الحد الأقصى لقيمة الكومة والحد الأقصى لقيمة الكومة غير الحد الأقصى للذاكرة الفعلية أو نظام التشغيل، فلن يبدأ الخادم.
نصيحة: قم بتعيين NewSize وMaxNewSize ليكونا متساويين، ويجب ألا يكون حجم "الجديد" أكبر من نصف "القديم". والسبب هو أنه إذا لم تكن المنطقة القديمة كبيرة بما يكفي، فسيتم تشغيل GC "الرئيسي" بشكل متكرر ، مما يقلل من الأداء بشكل كبير.
يستخدم JVM -XX:PermSize لتعيين القيمة الأولية للذاكرة غير الكومة. القيمة الافتراضية هي 1/64 من الذاكرة الفعلية؛
يتم تعيين الحد الأقصى لحجم الذاكرة غير الكومة بواسطة XX:MaxPermSize الافتراضي هو 1/4 من الذاكرة الفعلية.
الحل: قم بتعيين حجم الكومة يدويًا
تعديل TOMCAT_HOME/bin/catalina.bat
أضف الأسطر التالية فوق "echo "باستخدام CATALINA_BASE: $CATALINA_BASE"":
JAVA_OPTS="-الخادم -Xms800m -Xmx800m -XX:MaxNewSize=256m"
4. استخدم أدوات فحص الأداء لتحديد موقع تسرب الذاكرة:
تُستخدم أداة JProfiler بشكل أساسي لفحص وتتبع أداء الأنظمة (يقتصر على تطوير Java). يمكن لـ JProfiler مراقبة تشغيل JVM وأدائه من خلال المراقبة المستمرة لاستخدام ذاكرة النظام وجمع البيانات المهملة وحالة تشغيل الخيط والوسائل الأخرى في أي وقت.
1. ذاكرة خادم التطبيق مشغولة بشكل غير معقول لفترة طويلة. غالبًا ما تكون الذاكرة مشغولة بمستوى عالٍ ويصعب استعادتها إلى مستوى منخفض.