تنص مواصفات الجهاز الظاهري Java على أن يتم تقسيم ذاكرة JVM إلى عدة كتل ، مثل الكومة ، المكدس ، عداد البرنامج ، مساحة الطريقة ، إلخ. ينفذ النطاق المستمر منطقة الطريقة المحددة في المواصفات ، وستكون أجزاء مختلفة من نموذج الذاكرة لها أخطاء OutOfMemoryError المقابلة. بعد ذلك ، دعنا نناقشها بشكل منفصل. أعتقد أن معظم المطورين واجهوا هذا الخطأ ، وأسباب هذا الخطأ ناتج في الغالب عن الأسباب التالية:
ذاكرة JVM صغيرة جدًا والبرنامج ليس ضيقًا ، مما يؤدي إلى الكثير من القمامة.
هناك العديد من الأسباب الشائعة لاستثناءات OutofMemoryError:
مطالبات الخطأ الشائعة لهذا الخطأ:
Stackoverflowerror
stack Overflow يلقي خطأ java.lang.stackoverflowerror. يحدث هذا لأن عمق المكدس يتجاوز الحد الأقصى للعمق المسموح به بواسطة الجهاز الظاهري عند تشغيل الطريقة. يحدث هذا الموقف عادة بسبب أخطاء البرنامج. على سبيل المثال ، قد تسبب كتابة عودة ميتة هذا الموقف. دعنا نحاكي تدفق الذاكرة في هذا الموقف من خلال قطعة من الكود.
استيراد java.util.*؛ استيراد java.lang.*؛ الطبقة العامة oomtest {public void stackoverflowmethod () {stackoverflowmethod () ؛ } public static void main (string ... args) {oomtest oom = new OomTest () ؛ oom.stackoverflowmethod () ؛ }} تشغيل الكود أعلاه سوف يرمي الاستثناء التالي:
استثناء في الموضوع "الرئيسي" java.lang.stackoverflowerror في oomtest.stackoverflowmethod (oomtest.java:6)
فائض الكومة (OutofMemoryError: مساحة كومة جافا)
عندما تفيض ذاكرة الكومة ، يرمي الجهاز الظاهري java.lang.outofmemoryerror: مساحة كومة Java. عندما يحدث هذا ، نحتاج إلى تحليله استنادًا إلى ملف التفريغ الذي تم إنشاؤه عندما تفيض الذاكرة (-xx: +heapdumponoutofMemoryErrorrorJvm ، يجب إضافة معلمة بدء التشغيل). عندما تحدث مثل هذه المشكلة ، قد يكون تسرب الذاكرة أو تجاوز الذاكرة.
إذا تسرب الذاكرة ، فنحن بحاجة إلى معرفة كيفية الرجوع إلى الكائن الذي تم تسريبه بواسطة جذر GC ، ثم تحليل سبب التسرب من خلال السلسلة المرجعية.
إذا كانت هناك مشكلة في الفائض في الذاكرة ، فغالبًا ما يكون البرنامج يحتاج إلى ذاكرة أكثر من الذاكرة التي نهيئها للجهاز الظاهري. في هذه الحالة ، يمكننا استخدام -XMX لحل هذه المشكلة.
أدناه نوضح تدفق هذا الموقف من خلال الكود التالي:
استيراد java.util.*؛ استيراد java.lang.*؛ الفئة العامة oomtest {public static void main (string ... args) {list <byte []> buffer = new ArrayList <byte []> () ؛ Buffer.Add (بايت جديد [10*1024*1024]) ؛ }} ندير الرمز أعلاه من خلال الأمر التالي:
java -verbose: gc -xmn10m -xms20m -xmx20m -xx:+printgc oomtest
يدخل البرنامج المعلومات التالية:
[GC 1180K-> 366K (19456K) ، 0.0037311 SECS] [Full GC 366K-> 330K (19456K) ، 0.0098740 SECS] oomtest.main (oomtest.java:7)
من نتائج الجري ، يمكننا أن نرى أن JVM أجرى GC Minor مرة واحدة ومرتين GC. من ناتج GC الرئيسية ، يمكن ملاحظة أن معدل استخدام المنطقة القديمة بعد GC هو 134 كيلو بايت ، ومجموعة البايت هي 10 أمتار ، مما يضيف ليكون أكبر من مساحة الجيل القديم ، لذلك يتم إلقاء استثناء. إذا تم ضبط -xms21m و -xmx21m ، فلن يتم تشغيل عملية GC ولن يكون هناك استثناء.
من خلال التجربة أعلاه ، تم التحقق من استنتاج من الجانب: عندما يكون الكائن أكبر من الذاكرة المتبقية للجيل الجديد ، سيتم وضعه مباشرة في الشيخوخة. عندما لا تزال الذاكرة المتبقية للشيخوخة غير قادرة على وضعها ، سيتم تشغيل مجموعة القمامة. إذا كان لا يزال لا يمكن وضعه بعد التجميع ، فسيتم إلقاء استثناء في التدفق في التدفق.
الفضاء بيرجين
نحن نعلم أن Hotspot JVM تنفذ منطقة الطريقة في مواصفات الجهاز الظاهري Java من خلال النطاقات المستمرة ، ويتم تخزين تجمع وقت التشغيل في منطقة الطريقة. لذلك ، قد يكون تدفق النطاق المستمر هو تدفق تجمع وقت التشغيل ، أو لا يتم إعادة تدوير كائنات الفئة المحفوظة في منطقة الطريقة في الوقت المناسب أو أن الذاكرة التي تشغلها معلومات الفصل تتجاوز تكويننا. عندما تفيض فرقة الثبات ، java.lang.outofmemoryerror: يتم إلقاء مساحة بيرجن.
قد أواجه هذه المشكلة في السيناريوهات التالية عند العمل.
عند استخدام النشر الساخن لبعض خوادم التطبيقات ، سنواجه النشر الساخن عدة مرات ونجد تدفقات الذاكرة. هذا لأنه بعد كل نشر ساخن ، لم يتم إلغاء تثبيت الفصل الأصلي.
إذا كان التطبيق نفسه أكبر ويتضمن المزيد من مكتبات الفصل ، فقد تحدث هذه المشكلة أيضًا عندما تكون الذاكرة التي نخصصها للنطاق الثابت (الذي تم تعيينه بواسطة -xx: permsize و -xx: maxpermsize) صغير نسبيًا.
تنفذ بعض أطر عمل الطرف الثالث ، مثل Spring و Hibernate ، بعض الوظائف المحسّنة من خلال تقنية توليد الرمز BYTECODE (مثل CGLIB) ، والتي قد تتطلب منطقة طريقة أكبر لتخزين ملفات فئة تم إنشاؤها ديناميكيًا.
نحن نعلم أن ثوابت السلسلة في جافا توضع في تجمع ثابت. عند تشغيل طريقة string.intern () ، فسوف تحقق ما إذا كانت الكائنات مساوية لهذه السلسلة مخزنة في التجمع الثابت. في حالة وجوده ، قم بإرجاع إشارة إلى الكائن مباشرة في التجمع الثابت. إذا لم يكن موجودًا ، فأضف هذه السلسلة إلى التجمع الثابت أولاً ، ثم أعد المرجع إلى السلسلة. ثم يمكننا محاكاة الفائض من المنطقة الثابتة أثناء وقت التشغيل من خلال الأسلوب. دعنا نحاكي هذا الموقف من خلال الكود التالي:
استيراد java.util.*؛ استيراد java.lang.*؛ الفئة العامة oomtest {public static void main (string ... args) {list <string> list = new ArrayList <string> () ؛ بينما (صواب) {list.add (uuid.randomuuid (). toString (). intern ()) ؛ }}}ندير الرمز أعلاه من خلال الأمر التالي:
java -verbose: gc -xmn5m -xms10m -xmx10m -xx: maxpermsize = 1m -xx:+printgc oomtest
يظهر الإدخال بعد الجري في الشكل أدناه:
استثناء في الموضوع "الرئيسي" java.lang.outofmemoryerror: Permgen Space at Java.lang.string.intern (الطريقة الأصلية) في oomtest.main (oomtest.java:8)
من خلال الكود أعلاه ، نجحنا في محاكاة تدفق التجمع الثابت أثناء وقت التشغيل. من مساحة permgen في الإخراج ، يمكننا أن نرى أن النطاق المستمر يفيض بالفعل ، والذي يتحقق أيضًا من العبارة التي تفيد بأن النقطة الساخنة JVM تنفذ منطقة الطريقة من خلال النطاق المستمر كما ذكرنا سابقًا.
OutofMemoryerror: غير قادر على إنشاء موضوع أصلي
أخيرًا ، دعونا نلقي نظرة على الخطأ java.lang.outofmemoryerror: غير قادر على إنشاء مؤشر ترابط Natvie. عندما يحدث هذا ، يكون سبب ذلك عادةً في حالتين التاليين:
يتجاوز عدد مؤشرات الترابط التي تم إنشاؤها بواسطة البرنامج حد نظام التشغيل. بالنسبة لأنظمة Linux ، يمكننا عرض هذا القيد من خلال Ulimit -u.
تكون الذاكرة المخصصة للجهاز الظاهري كبيرًا جدًا ، مما يؤدي إلى القليل من الذاكرة الأصلية المطلوبة عند إنشاء مؤشرات الترابط. نعلم جميعًا أن نظام التشغيل لديه حد لذاكرة كل عملية. عند بدء JVM ، فإنه يعادل بدء العملية. إذا كانت إحدى عملياتنا تشغل 4G من الذاكرة ، فإن الذاكرة المتبقية المحسوبة من خلال الصيغة التالية هي الذاكرة التي يمكن استخدامها عند إنشاء مكدس مؤشر ترابط. إجمالي الذاكرة المتاحة لمكدس مؤشر الترابط = 4G- (قيمة -xmx)-(-xx: قيمة maxpermsize)-يتم عرض الذاكرة التي يشغلها عداد البرنامج بواسطة الصيغة أعلاه التي كلما زادت قيم -xmx و maxpermsize ، أصغر المساحة المتاحة لمكدس مؤشر الترابط. عندما تظل سعة المكدس التي تم تكوينها بواسطة المعلمة -XSS دون تغيير ، فكلما كان عدد مؤشرات الترابط الأصغر. لذلك ، إذا كان من المستحيل إنشاء مؤشر ترابط أصلي بسبب هذا الموقف ، فإننا إما نزيد من إجمالي الذاكرة التي تشغلها العملية ، أو تقليل -xmx أو -xss لتحقيق الغرض من إنشاء المزيد من مؤشرات الترابط.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.