ข้อกำหนดของเครื่องเสมือน Java กำหนดว่าหน่วยความจำ JVM ถูกแบ่งออกเป็นหลายบล็อกเช่นกองซ้อนสแต็กเคาน์เตอร์โปรแกรมพื้นที่วิธีการ ฯลฯ ในการใช้งานฮอตสปอต JVM หน่วยความจำกองแบ่งออกเป็นสามส่วนรุ่นใหม่อายุและวงดนตรีที่ถาวร แถบถาวรใช้พื้นที่วิธีการที่ระบุไว้ในข้อกำหนดและส่วนต่าง ๆ ของโมเดลหน่วยความจำจะมีข้อผิดพลาด outofMemoryError ที่สอดคล้องกัน ถัดไปเรามาคุยกันแยกกัน ฉันเชื่อว่านักพัฒนาส่วนใหญ่พบข้อผิดพลาดนี้และสาเหตุของข้อผิดพลาดนี้ส่วนใหญ่เกิดจากเหตุผลต่อไปนี้:
หน่วยความจำ JVM มีขนาดเล็กเกินไปและโปรแกรมไม่แน่นส่งผลให้ขยะมากเกินไป
มีหลายสาเหตุที่พบบ่อยของข้อยกเว้น outofmemoryError:
ข้อผิดพลาดทั่วไปแจ้งสำหรับข้อผิดพลาดนี้:
Stackoverflowerror
สแต็คล้นโยน java.lang.stackoverflowerror ข้อผิดพลาด สิ่งนี้เกิดขึ้นเนื่องจากความลึกของสแต็กเกินความลึกสูงสุดที่อนุญาตโดยเครื่องเสมือนเมื่อวิธีการทำงาน สถานการณ์นี้มักเกิดขึ้นเนื่องจากข้อผิดพลาดของโปรแกรม ตัวอย่างเช่นการเขียนการเรียกซ้ำที่ตายแล้วอาจทำให้สถานการณ์นี้ มาจำลองหน่วยความจำล้นในสถานการณ์นี้ผ่านรหัสชิ้นหนึ่ง
นำเข้า Java.util.*; นำเข้า Java.lang.*; ชั้นเรียนสาธารณะ oomtest {โมฆะสาธารณะ stackoverflowmethod () {stackoverflowmethod (); } โมฆะคงที่สาธารณะหลัก (สตริง ... args) {oomtest oom = new oomtest (); oom.stackoverflowmethod (); - การรันรหัสด้านบนจะโยนข้อยกเว้นดังต่อไปนี้:
ข้อยกเว้นใน Thread "Main" Java.lang.stackoverflowerror ที่ oomtest.stackoverflowmethod (oomtest.java:6)
HEAP Overflow (OutofMemoryError: พื้นที่ Java Heap)
เมื่อหน่วยความจำฮีปล้นเครื่องเสมือนจะโยน java.lang.outofmemoryError: พื้นที่ java heap เมื่อสิ่งนี้เกิดขึ้นเราจำเป็นต้องวิเคราะห์โดยเฉพาะตามไฟล์ Dump ที่สร้างขึ้นเมื่อหน่วยความจำล้น (-xx: +HEAPDUMPONOUTOFMEMORYERRORJVM พารามิเตอร์เริ่มต้นจะต้องเพิ่ม) เมื่อปัญหาดังกล่าวเกิดขึ้นอาจเป็นการรั่วไหลของหน่วยความจำหรือหน่วยความจำล้น
หากหน่วยความจำรั่วเราจำเป็นต้องค้นหาว่าวัตถุที่รั่วไหลออกมานั้นอ้างอิงโดยรูท GC แล้ววิเคราะห์สาเหตุของการรั่วไหลผ่านห่วงโซ่อ้างอิง
หากมีปัญหาการไหลล้นหน่วยความจำมักจะเป็นเพราะโปรแกรมต้องการหน่วยความจำมากกว่าหน่วยความจำที่เรากำหนดค่าสำหรับเครื่องเสมือน ในกรณีนี้เราสามารถใช้ -xmx เพื่อแก้ปัญหานี้
ด้านล่างเราแสดงให้เห็นถึงการล้นของสถานการณ์นี้ผ่านรหัสต่อไปนี้:
นำเข้า Java.util.*; นำเข้า Java.lang.*; คลาสสาธารณะ oomtest {โมฆะสาธารณะคงที่หลัก (สตริง ... 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 วินาที] [เต็ม GC 366K-> 330K (19456K), 0.0098740 วินาที] [Full GC 330K-> 292K (19456K), 0.009024444444 oomtest.main (oomtest.java:7)
จากผลการดำเนินงานเราจะเห็นว่า JVM ดำเนินการ GC เล็กน้อยหนึ่งครั้งและสองครั้งที่สำคัญ GC จากผลลัพธ์ของ Major GC จะเห็นได้ว่าอัตราการใช้งานของพื้นที่เก่าหลังจาก GC คือ 134K และอาร์เรย์ไบต์คือ 10m ซึ่งเพิ่มขึ้นมากกว่าพื้นที่รุ่นเก่าดังนั้นจึงมีข้อยกเว้น หากมีการปรับ -xms21m และ -xmx21m การดำเนินการ GC จะไม่ถูกทริกเกอร์และจะไม่มีข้อยกเว้น
ผ่านการทดลองข้างต้นข้อสรุปได้รับการตรวจสอบจากด้านข้าง: เมื่อวัตถุมีขนาดใหญ่กว่าหน่วยความจำที่เหลือของคนรุ่นใหม่มันจะถูกวางลงในวัยชราโดยตรง เมื่อความทรงจำที่เหลือของวัยชรายังไม่สามารถวางลงได้การรวบรวมขยะจะถูกกระตุ้น หากยังคงไม่สามารถวางลงได้หลังจากคอลเลกชันข้อยกเว้นของหน่วยความจำล้นจะถูกโยนลงไป
พื้นที่ permgen
เรารู้ว่าฮอตสปอต JVM ใช้พื้นที่วิธีการในข้อกำหนดของเครื่องเสมือน Java ผ่านแถบถาวรและสระว่ายน้ำคงที่รันไทม์จะถูกเก็บไว้ในพื้นที่วิธีการ ดังนั้นการล้นของแถบถาวรอาจเป็นสระว่ายน้ำคงที่รันไทม์หรือวัตถุคลาสที่บันทึกไว้ในพื้นที่วิธีการไม่ได้นำกลับมาใช้ใหม่ในเวลาหรือหน่วยความจำที่ถูกครอบครองโดยข้อมูลชั้นเรียนเกินการกำหนดค่าของเรา เมื่อวงดนตรีคงอยู่ล้น, java.lang.outofMemoryError: พื้นที่ Permgen ถูกโยนลงไป
ฉันอาจประสบปัญหานี้ในสถานการณ์ต่อไปนี้เมื่อทำงาน
เมื่อใช้การปรับใช้อย่างร้อนแรงของแอปพลิเคชันเซิร์ฟเวอร์บางตัวเราจะพบกับการปรับใช้ที่ร้อนแรงหลายครั้งและค้นหาหน่วยความจำที่ล้น นี่เป็นเพราะหลังจากการปรับใช้ที่ร้อนแต่ละครั้งคลาสดั้งเดิมไม่ได้ถูกถอนการติดตั้ง
หากแอปพลิเคชันมีขนาดใหญ่ขึ้นและเกี่ยวข้องกับไลบรารีคลาสมากขึ้นปัญหานี้อาจเกิดขึ้นได้เมื่อหน่วยความจำเราจัดสรรให้กับแถบถาวร (ตั้งค่าโดย -xx: Permsize และ -xx: Maxpermsize) ค่อนข้างเล็ก
เฟรมเวิร์กของบุคคลที่สามบางอย่างเช่นฤดูใบไม้ผลิและไฮเบอร์เนตทั้งหมดใช้ฟังก์ชั่นที่ได้รับการปรับปรุงบางอย่างผ่านเทคโนโลยีการสร้างไบต์ (เช่น CGLIB) ซึ่งอาจต้องใช้พื้นที่วิธีการขนาดใหญ่กว่าในการจัดเก็บไฟล์คลาสที่สร้างขึ้นแบบไดนามิก
เรารู้ว่าค่าคงที่สตริงในชวาถูกวางไว้ในสระคงที่ เมื่อวิธีการเรียกใช้ String.intern () มันจะตรวจสอบว่าวัตถุเท่ากับสตริงนี้จะถูกเก็บไว้ในพูลคงที่หรือไม่ หากมีอยู่ให้ส่งคืนการอ้างอิงโดยตรงไปยังวัตถุในพูลคงที่ หากไม่มีอยู่ให้เพิ่มสตริงนี้ลงในพูลคงที่ก่อนจากนั้นส่งคืนการอ้างอิงไปยังสตริง จากนั้นเราสามารถจำลองการล้นของพื้นที่คงที่ในระหว่างการรันไทม์ผ่านวิธีการสตริง มาจำลองสถานการณ์นี้ผ่านรหัสต่อไปนี้:
นำเข้า Java.util.*; นำเข้า Java.lang.*; คลาสสาธารณะ oomtest {โมฆะสาธารณะคงที่หลัก (สตริง ... 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
อินพุตหลังจากการทำงานจะแสดงในรูปด้านล่าง:
ข้อยกเว้นในเธรด "Main Main" java.lang.outofMemoryError: พื้นที่ permgen ที่ 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 มากขึ้น