يتم استخدام كومة Java لتخزين مثيلات الكائنات. لذلك ، إذا قمنا بإنشاء كائنات مستمرة وتأكد من وجود مسار يمكن الوصول إليه بين جذر GC والكائن الذي تم إنشاؤه لتجنب الكائن الذي يتم جمعه ، عند إنشاء الكثير من الكائنات ، فإنه سيؤدي إلى عدم كفاية ذاكرة كومة ، والتي ستثير استثناءًا من OutOfmoryerror.
/** * Author xiongyongshun * vm args: java -xms10m -xmx10m -xx:+heapdumponoutofmemoryerror */public class outofmemoryerrortest {public static void main (string) {list <Steger> list = new arraylist <> () ؛ int i = 0 ؛ بينما (صواب) {list.add (i ++) ؛ }}}ما سبق هو رمز يثير استثناء OutofMemoryError. يمكننا أن نرى أنه يمنع الكائن من أن يتم جمع القمامة عن طريق إنشاء الكائن وحفظه باستمرار في القائمة. لذلك ، عندما يكون هناك الكثير من الكائنات ، فإن ذاكرة الكومة ستتفوق.
من خلال java -xms10m -xmx10m -xx:+heapdumponoutofmemoryerror ، قمنا بتعيين ذاكرة الكومة على 10 ميغابايت ، ونستخدم المعلمة -xx:+heapdumponoutoTofMemoryerror لجعل JVM يطبع لقطة الذاكرة الحالية عندما يكون استثناء outofmoror في وقت لاحق.
بعد تجميع وتشغيل الرمز أعلاه ، سيكون الإخراج التالي:
>>> java -xms10m -xmx10m -xx:+heapdumponoutofmemoryerror com.test.outofmemoryerrortest 16-10-02 23: 35java.lang.outofmemoryerror: java heap pacedumping to java_pid1810.hprof ... 0.125 ثانية] استثناء في الموضوع "الرئيسي" java.lang.outofmemoryerror: java Heap Space at java.util.arrays.copyof (arrays.java:3210) at java.util.arrays.copyof (arrays.java:3181) at java.Uray. java.util.arraylist.ensureexplicitcapacity (ArrayList.java:235) java.util.arraylist.ensureCapacityIntern com.test.outofmemoryerrortest.main (OutofMemoryErrortest.java:15)
Java Stackoverflowerror
نحن نعلم أن هناك منطقة ذاكرة تسمى مكدس الجهاز الظاهري في منطقة بيانات وقت التشغيل في JVM. وظيفة هذه المنطقة هي: ستقوم كل طريقة بإنشاء إطار مكدس عند تنفيذه ، والذي يتم استخدامه لتخزين المعلومات مثل الجداول المتغيرة المحلية ، ومكدس المعامل ، وخارج الطريقة ، إلخ.
لذلك ، يمكننا إنشاء مكالمة عودية متكررة بلا حدود. عندما يكون العمق المتكرر كبيرًا جدًا ، سيتم استنفاد مساحة المكدس ، مما سيؤدي إلى استثناء StackOverFlowerror.
هنا هو الرمز المحدد:
/** * Author Xiongyongshun * VM args: Java -xss64k */public class OutOfMemoryerRortest {public static void main (string [] args) {StackoutofMemoryerror (1) ؛ } stackoutofMemoryerror static static static void (int depth) {depth ++ ؛ stackoutofmemoryerror (العمق) ؛ }}بعد تجميع وتشغيل الكود أعلاه ، سيتم إخراج معلومات الاستثناء التالية:
استثناء في الموضوع "الرئيسي" java.lang.stackoverflowerror في com.test.outofmemoryerrortest.stackoutofmemoryerror (OutofMemoryerRortest.java:27)
فائض الذاكرة في منطقة الطريقة
لاحظ أنه نظرًا لأن JDK8 قد أزال الجيل الدائم واستبدله بـ MetAspace ، في JDK8 ، لن يسبب أي من الأمثلة التالية java.lang.outofmemoryerror: Permgen Space Exception.
تدفقات حمام سباحة ثابتة أثناء وقت التشغيل
في Java 1.6 والإصدارات السابقة من نقطة Hotspot JVM ، هناك مفهوم للجيل الدائم ، أي أن آلية جمع التوليد في GC تمتد إلى منطقة الطريقة. في منطقة الطريقة ، يتم استخدام جزء من الذاكرة لتخزين حمامات السباحة الثابتة. لذلك ، إذا كان هناك الكثير من الثوابت في الكود ، فسيتم استنفاد ذاكرة التجمع الثابت ، مما يؤدي إلى تجاوز الذاكرة. إذن كيف تضيف عددًا كبيرًا من الثوابت إلى البركة الثابتة؟ في هذا الوقت ، تحتاج إلى الاعتماد على طريقة string.intern (). وظيفة طريقة string.intern () هي: إذا كانت قيمة هذه السلسلة موجودة بالفعل في التجمع الثابت ، فإن هذه الطريقة تُرجع الإشارة إلى السلسلة المقابلة في التجمع الثابت ؛ خلاف ذلك ، أضف القيمة الواردة في هذه السلسلة إلى التجمع الثابت وإرجاع المرجع إلى كائن السلسلة هذا. في JDK 1.6 والإصدارات السابقة ، يتم تخصيص تجمع ثابت في الجيل الدائم. لذلك ، يمكننا الحد بشكل غير مباشر من حجم التجمع الثابت عن طريق ضبط المعلمات "-xx: permsize" و "-xx: maxpermsize".
لاحظ أن توزيع الذاكرة للسلسلة. في JDK 1.7 أو أعلى ، يختلف تخطيط الذاكرة قليلاً لأنه تتم إزالة مفهوم الجيل الدائم.
فيما يلي مثال على الكود لتنفيذ تدفق الذاكرة من التجمعات الثابتة:
/** * Author xiongyongshun * vm args: -xx: permsize = 10m -xx: maxpermsize = 10m */public class runtimeconstantpooloomtest {public static void main (string [] args) {list <string> list = new ArrayList <string> () ؛ int i = 0 ؛ بينما (true) {list.add (string.valueof (i ++). intern ()) ؛ }}}نرى أنه في هذا المثال ، يستخدم على وجه التحديد طريقة string.intern () لإضافة عدد كبير من ثوابت السلسلة إلى البركة الثابتة ، مما يؤدي إلى تدفق الذاكرة في التجمع الثابت.
نقوم بتجميع الكود أعلاه وتشغيله من خلال JDK1.6 ، وسيكون الإخراج التالي:
استثناء في الموضوع "Main" java.lang.outofmemoryerror: Permgen Space في java.lang.string.intern (الطريقة الأصلية) في com.test.runtimeconstantpooloomtest.main (runtimeconstantpooloomtest.java:16)
تجدر الإشارة إلى أنه إذا تم تجميع الرمز أعلاه وتشغيله من خلال JDK1.8 ، فسيكون هناك تحذير التالي ولن يتم إنشاء استثناء:
>>> java -xx: permsize = 10m -xx: maxpermsize = 10m com.test.runtimeconstantpooloomtest 16-10-03 0: 23java hotspot (tm) 64-bit server vm تحذير: تجاهل permsize = 10m ؛ تمت إزالة الدعم في 8.0 Java Hotspot (TM) 64 بت خادم VM تحذير: تجاهل الخيار maxpermsize = 10m ؛ تمت إزالة الدعم في 8.0
تفوق الذاكرة في منطقة الطريقة
تتمثل وظيفة منطقة الأسلوب في تخزين المعلومات ذات الصلة للفئة ، مثل أسماء الفصول ، ومعدلات الوصول إلى الفئة ، وأوصاف الحقل ، وأوصاف الطريقة ، وما إلى ذلك ، إذا كانت منطقة الطريقة صغيرة جدًا وتم تحميل العديد من الفئات ، فزيادة الذاكرة في منطقة الطريقة.
] ensancer.SetSuperClass (MethodAreaoomTest.class) ؛ ensancer.setuseCache (false) ؛ ensancer.setCallback (new methodInterceptor () {اعتراض الكائن العام (الكائن O ، طريقة الطريقة ، الكائن ، الكائنات ، methodproxy methodproxy) يلقي رمي {return methodproxy.invokesuper (o ، Objects) ؛}}) ؛ ensancer.create () ؛ }}}في الكود أعلاه ، نستخدم CGLIB لإنشاء عدد كبير من الفئات بشكل ديناميكي. في JDK6 ، سيؤدي تشغيل الكود أعلاه إلى إنشاء OutofMemoryError: Permgen Space Exception:
/system/library/frameworks/javavm.framework/versions/1.6/home/bin/java-jar-xx: permsize = 10m -xx: maxpermsize = 10m target/test-1.0-snapshot.jar
نتيجة الإخراج على النحو التالي:
ناتج عن: java.lang.outofmemoryerror: permgen Space at java.lang.classloader.defineclass1 (الطريقة الأصلية) على java.lang.classloader.defineclasscond (classloader.java:637) على java.lang.classloader.definecl (classloader.java.java:621)
Metaspace Overflow
في قسم تدفق الذاكرة في تدفق الذاكرة في منطقة الطريقة ، ذكرنا أن JDK8 ليس لديه مفهوم للجيل الدائم ، لذلك لم يحقق هذان الأمثلان التأثير المتوقع بموجب JDK8. إذن في JDK8 ، هل هناك أي أخطاء مثل تجاوز الذاكرة في منطقة الطريقة؟ بالطبع ، بعض. في JDK8 ، تُستخدم منطقة Metaspace لتخزين المعلومات المتعلقة بالفئة ، لذلك عندما يكون MetAspace غير كافٍ ، سيتم طرح java.lang.outofmemoryerror: سيتم طرح استثناء Metaspace.
دعنا نأخذ المثال المذكور أعلاه كمثال:
] ensancer.SetSuperClass (MethodAreaoomTest.class) ؛ ensancer.setuseCache (false) ؛ ensancer.setCallback (new methodInterceptor () {اعتراض الكائن العام (الكائن O ، طريقة الطريقة ، الكائن ، الكائنات ، methodproxy methodproxy) يلقي رمي {return methodproxy.invokesuper (o ، Objects) ؛}}) ؛ ensancer.create () ؛ }}}لم يتم تغيير جزء الكود من هذا المثال. الفرق الوحيد هو أننا نحتاج إلى استخدام JDK8 لتشغيل هذا الرمز ، وتعيين المعلمة -xx: maxMeTaspAdesize = 10M. تخبر هذه المعلمة JVM أن الحد الأقصى لحجم MetAspace هو 10 أمتار.
ثم نستخدم JDK8 لتجميع وتشغيل هذا المثال ، وإخراج الاستثناء التالي:
>>> java -jar -xx: maxmetaspaceize = 10m target/test -1.0 -snapshot.jarexception in thread "main" net.sf.cglib.proxy.enhancer.generate (ensancer.java:492) في net.sf.cglib.core.abstractClassGenerator $ classloaderdata.get (AbstractClassGenerator.java:114) في net.sf.cglib.core.abstractClassGenerator.Create (AbstractClassGenerator.java:291) في net.sf.cglib.proxy.enhancer.createHelper (ensancer.java:480) في net.sf.cglib.proxy.enhance com.test.methodareaoomtest.main (MethodAreaoomtest.java:22)
لخص
ما سبق هو كل شيء عن استثناءات الفائض الشائع للذاكرة وأمثلة رمز في هذه المقالة ، وآمل أن تكون مفيدة للجميع. يمكن للأصدقاء المهتمين الاستمرار في الرجوع إلى الموضوعات الأخرى ذات الصلة على هذا الموقع. إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها. شكرا لك يا أصدقائك لدعمكم لهذا الموقع!