โปรแกรมเมอร์ที่ไม่มีประสบการณ์มักคิดว่าการรวบรวมขยะอัตโนมัติของ Java ช่วยให้พวกเขาหมดกังวลเรื่องการจัดการหน่วยความจำ นี่เป็นความเข้าใจผิดที่พบบ่อย: ในขณะที่พนักงานเก็บขยะทำได้ดีที่สุด แต่ก็เป็นไปได้ทั้งหมดที่โปรแกรมเมอร์ที่ดีที่สุดจะตกเป็นเหยื่อของการรั่วไหลของหน่วยความจำที่ทำให้พิการ ให้ฉันอธิบาย
การรั่วไหลของหน่วยความจำเกิดขึ้นเมื่อการอ้างอิงอ็อบเจ็กต์ที่ไม่จำเป็นอีกต่อไปได้รับการบำรุงรักษาโดยไม่จำเป็น การรั่วไหลเหล่านี้คือ ไม่ดี . ประการแรกพวกเขาสร้างความกดดันโดยไม่จำเป็นให้กับเครื่องของคุณเนื่องจากโปรแกรมของคุณใช้ทรัพยากรมากขึ้นเรื่อย ๆ เพื่อให้สิ่งต่างๆแย่ลงการตรวจจับการรั่วไหลเหล่านี้อาจเป็นเรื่องยาก: การวิเคราะห์แบบคงที่มักจะต้องดิ้นรนเพื่อระบุการอ้างอิงที่ซ้ำซ้อนเหล่านี้อย่างแม่นยำและเครื่องมือตรวจจับการรั่วไหลที่มีอยู่จะติดตามและรายงานข้อมูลที่ละเอียดเกี่ยวกับวัตถุแต่ละชิ้นทำให้ได้ผลลัพธ์ที่ยากต่อการตีความและขาดความแม่นยำ
กล่าวอีกนัยหนึ่งการรั่วไหลนั้นยากเกินไปที่จะระบุหรือระบุในรูปแบบที่เฉพาะเจาะจงเกินไปที่จะเป็นประโยชน์
ปัญหาหน่วยความจำมีสี่ประเภทที่มีอาการคล้ายกันและทับซ้อนกัน แต่สาเหตุและวิธีแก้ไขต่างกัน:
ประสิทธิภาพ : มักจะเกี่ยวข้องกับการสร้างและลบอ็อบเจ็กต์มากเกินไปความล่าช้าในการรวบรวมขยะเป็นเวลานานการสลับหน้าระบบปฏิบัติการมากเกินไปและอื่น ๆ
ข้อ จำกัด ด้านทรัพยากร : เกิดขึ้นเมื่อมีหน่วยความจำเหลือเพียงเล็กน้อยหรือหน่วยความจำของคุณกระจัดกระจายเกินกว่าที่จะจัดสรรวัตถุขนาดใหญ่ซึ่งอาจเป็นแบบเนทีฟหรือโดยทั่วไปแล้วก็คือ Java heap-related
การรั่วไหลของฮีป Java : การรั่วไหลของหน่วยความจำแบบคลาสสิกซึ่งวัตถุ Java ถูกสร้างขึ้นอย่างต่อเนื่องโดยไม่ปล่อยออกมา ซึ่งมักเกิดจากการอ้างอิงวัตถุแฝง
หน่วยความจำดั้งเดิมรั่วไหล : เชื่อมโยงกับการใช้หน่วยความจำที่เพิ่มขึ้นอย่างต่อเนื่องซึ่งอยู่นอกฮีป Java เช่นการจัดสรรที่ทำโดยรหัส JNI ไดรเวอร์หรือแม้แต่การจัดสรร JVM
ในบทช่วยสอนการจัดการหน่วยความจำนี้ฉันจะเน้นไปที่การรั่วไหลของฮีป Java และสรุปแนวทางในการตรวจจับการรั่วไหลตาม Java VisualVM รายงานและใช้อินเทอร์เฟซภาพสำหรับการวิเคราะห์ Java แอปพลิเคชันที่ใช้เทคโนโลยีในขณะที่กำลังทำงาน
แต่ก่อนที่คุณจะสามารถป้องกันและค้นหาการรั่วไหลของหน่วยความจำคุณควรเข้าใจว่าเกิดขึ้นได้อย่างไรและทำไม ( หมายเหตุ: หากคุณสามารถจัดการกับความซับซ้อนของการรั่วไหลของหน่วยความจำได้ดีคุณสามารถทำได้ ข้ามไปข้างหน้า . )
สำหรับผู้เริ่มต้นคิดว่าการรั่วไหลของหน่วยความจำเป็นโรคและ Java's OutOfMemoryError
(OOM เพื่อความกะทัดรัด) เป็นอาการ แต่เช่นเดียวกับโรคใด ๆ OOM ทั้งหมดไม่ได้หมายความถึงการรั่วไหลของหน่วยความจำ : OOM อาจเกิดขึ้นได้เนื่องจากการสร้างตัวแปรท้องถิ่นจำนวนมากหรือเหตุการณ์อื่น ๆ ดังกล่าว ในทางกลับกัน, การรั่วไหลของหน่วยความจำทั้งหมดไม่จำเป็นต้องแสดงตัวเองว่าเป็น OOM โดยเฉพาะอย่างยิ่งในกรณีของแอปพลิเคชันเดสก์ท็อปหรือแอปพลิเคชันไคลเอนต์ (ซึ่งจะไม่ทำงานเป็นเวลานานโดยไม่รีสตาร์ท)
ทำไมการรั่วไหลเหล่านี้จึงแย่มาก? เหนือสิ่งอื่นใดบล็อกหน่วยความจำที่รั่วไหลระหว่างการทำงานของโปรแกรมมักจะลดระดับลง ประสิทธิภาพของระบบ เมื่อเวลาผ่านไปเนื่องจากบล็อกหน่วยความจำที่จัดสรร แต่ไม่ได้ใช้จะต้องถูกสลับออกเมื่อระบบไม่มีหน่วยความจำกายภาพที่ว่างเหลืออยู่ ในที่สุดโปรแกรมอาจใช้พื้นที่ที่อยู่เสมือนที่มีอยู่จนหมดซึ่งนำไปสู่ OOM
OutOfMemoryError
ดังที่ได้กล่าวมาแล้ว OOM เป็นข้อบ่งชี้ทั่วไปของการรั่วไหลของหน่วยความจำ โดยพื้นฐานแล้วข้อผิดพลาดจะเกิดขึ้นเมื่อมีพื้นที่ไม่เพียงพอที่จะจัดสรรวัตถุใหม่ ลองใช้เท่าที่จะทำได้เครื่องเก็บขยะไม่พบพื้นที่ที่จำเป็นและไม่สามารถขยายกองได้อีก ดังนั้นจึงมีข้อผิดพลาดเกิดขึ้นพร้อมกับไฟล์ การติดตามสแต็ก .
ขั้นตอนแรกในการวินิจฉัย OOM ของคุณคือการกำหนดความหมายของข้อผิดพลาด ฟังดูชัดเจน แต่คำตอบไม่ชัดเจนเสมอไป ตัวอย่าง: OOM ปรากฏขึ้นเนื่องจากฮีป Java เต็มหรือเนื่องจากฮีปเนทีฟเต็ม? เพื่อช่วยคุณตอบคำถามนี้เรามาวิเคราะห์ข้อความแสดงข้อผิดพลาดที่เป็นไปได้บางส่วน:
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: PermGen space
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
java.lang.OutOfMemoryError: request bytes for . Out of swap space?
java.lang.OutOfMemoryError: (Native method)
ข้อความแสดงข้อผิดพลาดนี้ไม่ได้หมายความถึงหน่วยความจำรั่วเสมอไป ในความเป็นจริงปัญหาอาจเป็นเรื่องง่ายเหมือนปัญหาการกำหนดค่า
ตัวอย่างเช่นฉันรับผิดชอบในการวิเคราะห์แอปพลิเคชันที่ผลิต OutOfMemoryError
ประเภทนี้อย่างต่อเนื่อง หลังจากการตรวจสอบบางส่วนฉันพบว่าผู้ร้ายคืออินสแตนซ์อาร์เรย์ที่ต้องการหน่วยความจำมากเกินไป ในกรณีนี้ไม่ใช่ความผิดพลาดของแอปพลิเคชัน แต่เป็นเพราะแอปพลิเคชันเซิร์ฟเวอร์อาศัยขนาดฮีปเริ่มต้นซึ่งเล็กเกินไป ฉันแก้ปัญหาโดยการปรับ พารามิเตอร์หน่วยความจำของ JVM .
ในกรณีอื่น ๆ และโดยเฉพาะอย่างยิ่งสำหรับแอปพลิเคชันที่มีอายุการใช้งานยาวนานข้อความอาจบ่งชี้ว่าเราไม่ได้ตั้งใจ ถือการอ้างอิงถึงวัตถุ ป้องกันไม่ให้คนเก็บขยะทำความสะอาด นี่คือภาษา Java เทียบเท่ากับการรั่วไหลของหน่วยความจำ . ( หมายเหตุ: API ที่เรียกโดยแอปพลิเคชันอาจถือการอ้างอิงอ็อบเจ็กต์โดยไม่ได้ตั้งใจ )
แหล่งที่มาที่เป็นไปได้อีกประการหนึ่งของ OOMs 'Java heap space' เหล่านี้เกิดขึ้นจากการใช้ Finalizers . ถ้าชั้นเรียนมี finalize
วิธีการจากนั้นวัตถุประเภทนั้นจะไม่มีการเรียกคืนพื้นที่ในเวลารวบรวมขยะ หลังจากการรวบรวมขยะวัตถุจะถูกจัดคิวสำหรับการสรุปซึ่งจะเกิดขึ้นในภายหลัง ในการนำไปใช้งาน Sun ผู้สรุปจะดำเนินการโดย a เธรด daemon . หากเธรด Finalizer ไม่สามารถทำตามคิวการปิดท้ายได้ฮีป Java สามารถเติมเต็มและ OOM อาจถูกโยนทิ้ง
ข้อความแสดงข้อผิดพลาดนี้ระบุว่าไฟล์ รุ่นถาวร เต็ม. การสร้างแบบถาวรคือพื้นที่ของฮีปที่เก็บคลาสและอ็อบเจ็กต์เมธอด หากแอปพลิเคชันโหลดคลาสจำนวนมากขนาดของรุ่นถาวรอาจต้องเพิ่มขึ้นโดยใช้ -XX:MaxPermSize
ตัวเลือก
ฝึกงาน java.lang.String
วัตถุจะถูกเก็บไว้ในรุ่นถาวรด้วย java.lang.String
คลาสรักษาพูลของสตริง เมื่อเรียกใช้เมธอด intern เมธอดจะตรวจสอบพูลเพื่อดูว่ามีสตริงที่เท่ากันหรือไม่ หากเป็นเช่นนั้นจะส่งคืนโดยวิธีการฝึกงาน หากไม่เป็นเช่นนั้นสตริงจะถูกเพิ่มลงในพูล ในแง่ที่แม่นยำยิ่งขึ้นคือ java.lang.String.intern
วิธีการคืนค่าสตริง การแสดงตามบัญญัติ ; ผลลัพธ์คือการอ้างอิงไปยังอินสแตนซ์คลาสเดียวกันที่จะถูกส่งกลับหากสตริงนั้นปรากฏเป็นลิเทอรัล หากแอปพลิเคชันใช้สตริงจำนวนมากคุณอาจต้องเพิ่มขนาดของการสร้างแบบถาวร
หมายเหตุ: คุณสามารถใช้ jmap -permgen
คำสั่งเพื่อพิมพ์สถิติที่เกี่ยวข้องกับการสร้างแบบถาวรรวมถึงข้อมูลเกี่ยวกับอินสแตนซ์ String ภายใน
ข้อผิดพลาดนี้บ่งชี้ว่าแอปพลิเคชัน (หรือ API ที่ใช้โดยแอปพลิเคชันนั้น) พยายามจัดสรรอาร์เรย์ที่มีขนาดใหญ่กว่าขนาดฮีป ตัวอย่างเช่นหากแอปพลิเคชันพยายามจัดสรรอาร์เรย์ 512MB แต่ขนาดฮีปสูงสุดคือ 256MB OOM จะแสดงข้อความแสดงข้อผิดพลาดนี้ ในกรณีส่วนใหญ่ปัญหาอาจเป็นปัญหาการกำหนดค่าหรือข้อบกพร่องที่เกิดขึ้นเมื่อแอปพลิเคชันพยายามจัดสรรอาร์เรย์จำนวนมาก
ข้อความนี้ดูเหมือนจะเป็น OOM อย่างไรก็ตาม HotSpot VM จะแสดงข้อยกเว้นที่ชัดเจนนี้เมื่อการจัดสรรจากฮีปเนทีฟล้มเหลวและฮีปเนทีฟอาจใกล้จะหมดลง ข้อความที่รวมอยู่ในข้อความคือขนาด (เป็นไบต์) ของคำขอที่ล้มเหลวและสาเหตุของการร้องขอหน่วยความจำ ในกรณีส่วนใหญ่ชื่อของโมดูลต้นทางที่รายงานการจัดสรรล้มเหลว
หาก OOM ประเภทนี้ถูกส่งออกไปคุณอาจต้องใช้ยูทิลิตี้การแก้ไขปัญหาบนระบบปฏิบัติการของคุณเพื่อวินิจฉัยปัญหาเพิ่มเติม ในบางกรณีปัญหาอาจไม่เกี่ยวข้องกับแอปพลิเคชันด้วยซ้ำ ตัวอย่างเช่นคุณอาจเห็นข้อผิดพลาดนี้หาก:
ระบบปฏิบัติการได้รับการกำหนดค่าโดยมีพื้นที่สวอปไม่เพียงพอ
กระบวนการอื่นในระบบกำลังใช้ทรัพยากรหน่วยความจำที่มีอยู่ทั้งหมด
นอกจากนี้ยังเป็นไปได้ว่าแอปพลิเคชันล้มเหลวเนื่องจากการรั่วไหลในระบบ (ตัวอย่างเช่นหากบิตของแอปพลิเคชันหรือรหัสไลบรารีบางส่วนจัดสรรหน่วยความจำอย่างต่อเนื่อง แต่ไม่สามารถปล่อยลงในระบบปฏิบัติการได้)
หากคุณเห็นข้อความแสดงข้อผิดพลาดนี้และกรอบด้านบนของการติดตามสแต็กของคุณเป็นวิธีดั้งเดิมแสดงว่าเมธอดดั้งเดิมนั้นประสบความล้มเหลวในการจัดสรร ความแตกต่างระหว่างข้อความนี้กับข้อความก่อนหน้าคือตรวจพบความล้มเหลวในการจัดสรรหน่วยความจำ Java ใน JNI หรือเมธอดดั้งเดิมแทนที่จะเป็นโค้ด Java VM
หาก OOM ประเภทนี้ถูกส่งออกไปคุณอาจต้องใช้ยูทิลิตี้บนระบบปฏิบัติการเพื่อวินิจฉัยปัญหาเพิ่มเติม
ในบางครั้งแอปพลิเคชันอาจหยุดทำงานในไม่ช้าหลังจากความล้มเหลวในการจัดสรรจากฮีปดั้งเดิม กรณีนี้เกิดขึ้นหากคุณกำลังเรียกใช้โค้ดเนทีฟที่ไม่ได้ตรวจสอบข้อผิดพลาดที่ส่งคืนโดยฟังก์ชันการจัดสรรหน่วยความจำ
ตัวอย่างเช่น malloc
การเรียกคืนระบบ NULL
หากไม่มีหน่วยความจำ ถ้าผลตอบแทนจาก malloc
ไม่ได้ตรวจสอบแอปพลิเคชันอาจหยุดทำงานเมื่อพยายามเข้าถึงตำแหน่งหน่วยความจำที่ไม่ถูกต้อง ปัญหาประเภทนี้อาจเป็นเรื่องยากในการค้นหาทั้งนี้ขึ้นอยู่กับสถานการณ์
ในบางกรณีข้อมูลจากบันทึกข้อผิดพลาดร้ายแรงหรือการถ่ายโอนข้อมูลข้อขัดข้องจะเพียงพอ หากสาเหตุของความผิดพลาดถูกพิจารณาว่าขาดการจัดการข้อผิดพลาดในการจัดสรรหน่วยความจำบางส่วนคุณต้องค้นหาสาเหตุของความล้มเหลวในการจัดสรรดังกล่าว เช่นเดียวกับปัญหาฮีปเนทีฟอื่น ๆ ระบบอาจได้รับการกำหนดค่าด้วยพื้นที่สวอปไม่เพียงพอกระบวนการอื่นอาจใช้ทรัพยากรหน่วยความจำที่มีอยู่ทั้งหมดเป็นต้น
ในกรณีส่วนใหญ่การวินิจฉัยการรั่วไหลของหน่วยความจำจำเป็นต้องมีความรู้โดยละเอียดเกี่ยวกับแอปพลิเคชันที่เป็นปัญหา คำเตือน: กระบวนการอาจมีความยาวและทำซ้ำได้
กลยุทธ์ของเราในการค้นหาการรั่วไหลของหน่วยความจำจะค่อนข้างตรงไปตรงมา:
ระบุอาการ
เปิดใช้งานการรวบรวมขยะแบบละเอียด
เปิดใช้งานการสร้างโปรไฟล์
วิเคราะห์การติดตาม
ตามที่กล่าวไว้ในหลาย ๆ กรณีกระบวนการ Java จะทำให้เกิดข้อยกเว้นรันไทม์ OOM ซึ่งเป็นตัวบ่งชี้ที่ชัดเจนว่าทรัพยากรหน่วยความจำของคุณหมดลงแล้ว ในกรณีนี้คุณต้องแยกความแตกต่างระหว่างความจำเสื่อมปกติและการรั่วไหล วิเคราะห์ข้อความของ OOM และพยายามค้นหาผู้กระทำผิดตามการอภิปรายที่ให้ไว้ข้างต้น
บ่อยครั้งหากแอ็พพลิเคชัน Java ร้องขอพื้นที่เก็บข้อมูลมากกว่าที่ฮีปรันไทม์เสนออาจเป็นเพราะการออกแบบที่ไม่ดี ตัวอย่างเช่นหากแอปพลิเคชันสร้างสำเนารูปภาพหลายชุดหรือโหลดไฟล์ลงในอาร์เรย์แอปพลิเคชันจะไม่มีพื้นที่เก็บข้อมูลเมื่อรูปภาพหรือไฟล์มีขนาดใหญ่มาก นี่เป็นความเหนื่อยล้าของทรัพยากรตามปกติ แอปพลิเคชันทำงานตามที่ออกแบบไว้ (แม้ว่าการออกแบบนี้จะมีหัวกระดูกอย่างชัดเจน)
แต่หากแอปพลิเคชันเพิ่มการใช้หน่วยความจำไปเรื่อย ๆ ในขณะที่ประมวลผลข้อมูลประเภทเดียวกันคุณอาจมีหน่วยความจำรั่ว
วิธีหนึ่งที่เร็วที่สุดในการยืนยันว่าคุณมีหน่วยความจำรั่วคือการเปิดใช้งานการรวบรวมขยะแบบละเอียด โดยทั่วไปปัญหาข้อ จำกัด ของหน่วยความจำสามารถระบุได้โดยการตรวจสอบรูปแบบใน verbosegc
เอาท์พุท
โดยเฉพาะไฟล์ -verbosegc
อาร์กิวเมนต์อนุญาตให้คุณสร้างการติดตามทุกครั้งที่กระบวนการรวบรวมขยะ (GC) เริ่มต้นขึ้น นั่นคือในขณะที่หน่วยความจำกำลังถูกเก็บรวบรวมเป็นขยะรายงานสรุปจะถูกพิมพ์เป็นข้อผิดพลาดมาตรฐานทำให้คุณทราบว่าหน่วยความจำของคุณได้รับการจัดการอย่างไร
นี่คือผลลัพธ์ทั่วไปบางส่วนที่สร้างขึ้นด้วย –verbosegc
ตัวเลือก:
แต่ละบล็อก (หรือ stanza) ในไฟล์การติดตาม GC นี้มีหมายเลขตามลำดับที่เพิ่มขึ้น เพื่อให้เข้าใจถึงร่องรอยนี้คุณควรดูที่บทวิเคราะห์ Allocation Failure ที่ต่อเนื่องกันและมองหาหน่วยความจำที่ว่าง (ไบต์และเปอร์เซ็นต์) ลดลงเมื่อเวลาผ่านไปในขณะที่หน่วยความจำทั้งหมด (ที่นี่ 19725304) กำลังเพิ่มขึ้น นี่เป็นสัญญาณทั่วไปของความจำพร่อง
JVM ที่แตกต่างกันเสนอวิธีต่างๆในการสร้างไฟล์การติดตามเพื่อสะท้อนกิจกรรมฮีปซึ่งโดยทั่วไปจะมีข้อมูลโดยละเอียดเกี่ยวกับประเภทและขนาดของวัตถุ นี้เรียกว่า การทำโปรไฟล์ฮีป .
โพสต์นี้มุ่งเน้นไปที่การติดตามที่สร้างโดย Java VisualVM การติดตามสามารถมาในรูปแบบที่แตกต่างกันเนื่องจากสามารถสร้างขึ้นโดยเครื่องมือตรวจจับการรั่วไหลของหน่วยความจำ Java ที่แตกต่างกัน แต่แนวคิดเบื้องหลังจะเหมือนกันเสมอ: ค้นหาบล็อกของวัตถุในฮีปที่ไม่ควรอยู่ที่นั่นและพิจารณาว่าวัตถุเหล่านี้สะสมอยู่หรือไม่ แทนที่จะปล่อย สิ่งที่น่าสนใจเป็นพิเศษคืออ็อบเจ็กต์ชั่วคราวที่ทราบว่าได้รับการจัดสรรทุกครั้งที่มีการทริกเกอร์เหตุการณ์บางอย่างในแอ็พพลิเคชัน Java การมีอยู่ของอินสแตนซ์ออบเจ็กต์จำนวนมากที่ควรมีอยู่ในปริมาณเล็กน้อยเท่านั้นโดยทั่วไปบ่งชี้ถึงจุดบกพร่องของแอปพลิเคชัน
สุดท้ายการแก้ปัญหาหน่วยความจำรั่วคุณต้องตรวจสอบโค้ดของคุณอย่างละเอียด การเรียนรู้เกี่ยวกับประเภทของการรั่วไหลของออบเจ็กต์จะมีประโยชน์มากและช่วยเร่งการแก้ไขจุดบกพร่องได้มาก
ก่อนที่เราจะเริ่มวิเคราะห์แอปพลิเคชันที่มีปัญหาหน่วยความจำรั่วก่อนอื่นเรามาดูวิธีการทำงานของการรวบรวมขยะใน JVM
JVM ใช้รูปแบบของตัวรวบรวมขยะที่เรียกว่า นักสะสมการติดตาม ซึ่งโดยพื้นฐานแล้วจะทำงานโดยการหยุดโลกรอบ ๆ ตัวชั่วคราวทำเครื่องหมายอ็อบเจ็กต์รูททั้งหมด (อ็อบเจ็กต์ที่อ้างอิงโดยตรงโดยรันเธรด) และทำตามการอ้างอิงโดยทำเครื่องหมายแต่ละอ็อบเจ็กต์ที่เห็นระหว่างทาง
Java ใช้สิ่งที่เรียกว่า รุ่น คนเก็บขยะตามสมมติฐานของเจเนอเรชั่นอลซึ่งระบุว่า วัตถุส่วนใหญ่ที่สร้างขึ้นจะถูกทิ้งอย่างรวดเร็ว และ วัตถุที่ไม่ได้รับการรวบรวมอย่างรวดเร็วน่าจะอยู่ในช่วงเวลาหนึ่ง .
จากสมมติฐานนี้ พาร์ติชัน Java ออบเจ็กต์เป็นหลายชั่วอายุคน . นี่คือการตีความภาพ:
คนรุ่นใหม่ - นี่คือจุดเริ่มต้นของวัตถุ มีสองรุ่นย่อย:
เอเดนสเปซ - วัตถุเริ่มต้นที่นี่ วัตถุส่วนใหญ่ถูกสร้างและทำลายใน Eden Space ที่นี่ GC ทำ GC เล็กน้อย ซึ่งเป็นการรวบรวมขยะที่เหมาะสมที่สุด เมื่อดำเนินการ Minor GC การอ้างอิงถึงอ็อบเจ็กต์ที่ยังจำเป็นจะถูกโอนย้ายไปยังช่องว่างผู้รอดชีวิต (S0 หรือ S1)
Survivor Space (S0 และ S1) - วัตถุที่อยู่รอดในสวนอีเดนจบลงที่นี่ มีสองสิ่งนี้และมีเพียงหนึ่งเดียวที่ถูกใช้งานในช่วงเวลาใดเวลาหนึ่ง (เว้นแต่เราจะมีการรั่วไหลของหน่วยความจำที่ร้ายแรง) หนึ่งถูกกำหนดให้เป็น ว่างเปล่า และอื่น ๆ เป็น มีชีวิต สลับกับทุกรอบ GC
รุ่นอายุยืน - หรือที่เรียกว่าคนรุ่นเก่า (พื้นที่เก่าในรูปที่ 2) พื้นที่นี้เก็บวัตถุรุ่นเก่าที่มีอายุการใช้งานยาวนานขึ้น (ย้ายจากช่องว่างของผู้รอดชีวิตหากมีชีวิตอยู่นานพอ) เมื่อเติมช่องว่างนี้ GC จะทำไฟล์ GC เต็มรูปแบบ ซึ่งมีค่าใช้จ่ายมากกว่าในแง่ของประสิทธิภาพ หากช่องว่างนี้เติบโตขึ้นโดยไม่มีขอบเขต JVM จะโยน OutOfMemoryError - Java heap space
รุ่นถาวร - รุ่นที่สามที่เกี่ยวข้องอย่างใกล้ชิดกับรุ่นที่ครอบครองรุ่นถาวรมีความพิเศษเนื่องจากเก็บข้อมูลที่เครื่องเสมือนต้องการเพื่ออธิบายวัตถุที่ไม่มีความเท่าเทียมกันในระดับภาษา Java ตัวอย่างเช่นอ็อบเจ็กต์ที่อธิบายคลาสและเมธอดจะถูกเก็บไว้ในการสร้างแบบถาวร
Java ฉลาดพอที่จะใช้วิธีการเก็บขยะที่แตกต่างกันไปในแต่ละรุ่น คนรุ่นใหม่ได้รับการจัดการโดยใช้ไฟล์ การติดตามการคัดลอกตัวรวบรวม เรียกว่า นักสะสมใหม่แบบขนาน . นักสะสมคนนี้หยุดโลก แต่เนื่องจากคนรุ่นใหม่มักตัวเล็กการหยุดชั่วคราวจึงสั้น
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับรุ่น JVM และวิธีการทำงานโดยละเอียดโปรดไปที่ การจัดการหน่วยความจำใน Java HotSpot Virtual Machine เอกสารประกอบ.
หากต้องการค้นหาการรั่วไหลของหน่วยความจำและกำจัดออกคุณต้องมีเครื่องมือหน่วยความจำรั่วที่เหมาะสม ได้เวลาตรวจจับและกำจัดการรั่วไหลโดยใช้ไฟล์ Java VisualVM .
VisualVM เป็นเครื่องมือที่มีอินเทอร์เฟซภาพสำหรับการดูข้อมูลโดยละเอียดเกี่ยวกับแอปพลิเคชันที่ใช้เทคโนโลยี Java ในขณะที่กำลังทำงาน
ด้วย VisualVM คุณสามารถดูข้อมูลที่เกี่ยวข้องกับแอปพลิเคชันภายในเครื่องและข้อมูลที่ทำงานบนโฮสต์ระยะไกล คุณยังสามารถเก็บข้อมูลเกี่ยวกับอินสแตนซ์ซอฟต์แวร์ JVM และบันทึกข้อมูลลงในระบบภายในของคุณ
เพื่อให้ได้รับประโยชน์จากคุณลักษณะทั้งหมดของ Java VisualVM คุณควรเรียกใช้ Java Platform, Standard Edition (Java SE) เวอร์ชัน 6 ขึ้นไป
ที่เกี่ยวข้อง: ทำไมคุณต้องอัพเกรดเป็น Java 8 อยู่แล้วในสภาพแวดล้อมการใช้งานจริงมักจะยากที่จะเข้าถึงเครื่องจริงที่จะใช้โค้ดของเรา โชคดีที่เราสามารถกำหนดโปรไฟล์แอปพลิเคชัน Java ของเราได้จากระยะไกล
ขั้นแรกเราต้องให้สิทธิ์ตัวเองในการเข้าถึง JVM บนเครื่องเป้าหมาย โดยสร้างไฟล์ชื่อ jstatd.all.policy โดยมีเนื้อหาดังต่อไปนี้:
grant codebase 'file:${java.home}/../lib/tools.jar' { permission java.security.AllPermission; };
เมื่อสร้างไฟล์แล้วเราจำเป็นต้องเปิดใช้งานการเชื่อมต่อระยะไกลไปยัง VM เป้าหมายโดยใช้ไฟล์ jstatd - เครื่องเสมือน jstat Daemon เครื่องมือดังต่อไปนี้:
jstatd -p -J-Djava.security.policy=
ตัวอย่างเช่น:
jstatd -p 1234 -J-Djava.security.policy=D:jstatd.all.policy
กับ jstatd เริ่มต้นใน VM เป้าหมายเราสามารถเชื่อมต่อกับเครื่องเป้าหมายและกำหนดโปรไฟล์แอปพลิเคชันจากระยะไกลที่มีปัญหาหน่วยความจำรั่ว
ในเครื่องไคลเอนต์เปิดพรอมต์แล้วพิมพ์ jvisualvm
เพื่อเปิดเครื่องมือ VisualVM
ต่อไปเราต้องเพิ่มโฮสต์ระยะไกลใน VisualVM เนื่องจาก JVM เป้าหมายถูกเปิดใช้งานเพื่ออนุญาตการเชื่อมต่อระยะไกลจากเครื่องอื่นที่มี J2SE 6 หรือสูงกว่าเราจึงเริ่มเครื่องมือ Java VisualVM และเชื่อมต่อกับโฮสต์ระยะไกล หากการเชื่อมต่อกับโฮสต์ระยะไกลประสบความสำเร็จเราจะเห็นแอปพลิเคชัน Java ที่ทำงานใน JVM เป้าหมายดังที่เห็นที่นี่:
ในการเรียกใช้ตัวสร้างโปรไฟล์หน่วยความจำบนแอปพลิเคชันเราเพียงดับเบิลคลิกที่ชื่อในแผงด้านข้าง
ตอนนี้เราได้ติดตั้งเครื่องวิเคราะห์หน่วยความจำเรียบร้อยแล้วเรามาตรวจสอบแอปพลิเคชันที่มีปัญหาหน่วยความจำรั่วซึ่งเราจะเรียกว่า MemLeak .
แน่นอนว่ามีหลายวิธีในการสร้างการรั่วไหลของหน่วยความจำใน Java เพื่อความง่ายเราจะกำหนดคลาสให้เป็นคีย์ใน a HashMap
แต่เราจะไม่กำหนด เท่ากับ () และ hashcode () วิธีการ
HashMap คือไฟล์ ตารางแฮช การนำไปใช้งานสำหรับอินเทอร์เฟซแผนที่และด้วยเหตุนี้จึงกำหนดแนวคิดพื้นฐานของคีย์และค่า: แต่ละค่าเกี่ยวข้องกับคีย์ที่ไม่ซ้ำกันดังนั้นหากคีย์สำหรับคู่คีย์ - ค่าที่กำหนดมีอยู่แล้วใน HashMap ค่าปัจจุบันจะเป็น แทนที่
จำเป็นต้องมีคลาสสำคัญของเราต้องนำ equals()
ไปใช้อย่างถูกต้อง และ hashcode()
วิธีการ หากไม่มีพวกเขาก็ไม่มีการรับประกันว่าจะมีการสร้างคีย์ที่ดี
โดยไม่กำหนด equals()
และ hashcode()
วิธีการเราเพิ่มคีย์เดียวกันลงใน HashMap ซ้ำแล้วซ้ำอีกและแทนที่จะแทนที่คีย์ตามที่ควรจะเป็น HashMap เติบโตขึ้นอย่างต่อเนื่องล้มเหลวในการระบุคีย์ที่เหมือนกันเหล่านี้และโยน OutOfMemoryError
นี่คือคลาส MemLeak:
package com.post.memory.leak; import java.util.Map; public class MemLeak { public final String key; public MemLeak(String key) { this.key =key; } public static void main(String args[]) { try { Map map = System.getProperties(); for(;;) { map.put(new MemLeak('key'), 'value'); } } catch(Exception e) { e.printStackTrace(); } } }
หมายเหตุ: หน่วยความจำรั่วคือ ไม่ เนื่องจากการวนซ้ำที่ไม่มีที่สิ้นสุดในบรรทัดที่ 14: การวนซ้ำแบบไม่มีที่สิ้นสุดสามารถนำไปสู่การหมดทรัพยากร แต่ไม่ใช่การรั่วไหลของหน่วยความจำ หากเรานำมาใช้อย่างถูกต้อง equals()
และ hashcode()
วิธีการโค้ดจะทำงานได้ดีแม้จะมีการวนซ้ำแบบไม่มีที่สิ้นสุดเนื่องจากเราจะมีเพียงองค์ประกอบเดียวใน HashMap
(สำหรับผู้ที่สนใจ ที่นี่ เป็นทางเลือกอื่นในการก่อให้เกิดการรั่วไหล (โดยเจตนา))
ด้วย Java VisualVM เราสามารถตรวจสอบหน่วยความจำ Java Heap และระบุว่าพฤติกรรมของมันบ่งบอกถึงการรั่วไหลของหน่วยความจำหรือไม่
นี่คือการนำเสนอแบบกราฟิกของตัววิเคราะห์ Java Heap ของ MemLeak หลังจากเริ่มต้นใช้งาน (นึกถึงการอภิปรายของเราเกี่ยวกับ ชั่วอายุคน ):
หลังจากผ่านไปเพียง 30 วินาที Old Generation ก็ใกล้จะเต็มแสดงให้เห็นว่าแม้จะมี GC เต็มรูปแบบ แต่ Old Generation ก็เติบโตขึ้นเรื่อย ๆ ซึ่งเป็นสัญญาณที่ชัดเจนของการรั่วไหลของหน่วยความจำ
วิธีหนึ่งในการตรวจหาสาเหตุของการรั่วไหลนี้แสดงในภาพต่อไปนี้ ( คลิกเพื่อซูม ) สร้างขึ้นโดยใช้ Java VisualVM พร้อมกับไฟล์ heapdump . ที่นี่เราเห็นว่า 50% ของวัตถุ Hashtable $ Entry อยู่ในฮีป ในขณะที่บรรทัดที่สองชี้เราไปที่ไฟล์ MemLeak ชั้นเรียน. ดังนั้นหน่วยความจำรั่วจึงเกิดจากไฟล์ ตารางแฮช ใช้ภายในไฟล์ MemLeak ชั้นเรียน.
สุดท้ายให้สังเกต Java Heap หลังจาก OutOfMemoryError
ของเรา ซึ่งใน เยาวชนและคนรุ่นเก่าเต็มไปหมด .
การรั่วไหลของหน่วยความจำเป็นหนึ่งในแอปพลิเคชัน Java ที่ยากที่สุด ปัญหาในการแก้ไข เนื่องจากอาการมีความหลากหลายและเกิดซ้ำได้ยาก ที่นี่เราได้สรุปวิธีการทีละขั้นตอนในการค้นหาการรั่วไหลของหน่วยความจำและระบุแหล่งที่มา แต่เหนือสิ่งอื่นใดอ่านข้อความแสดงข้อผิดพลาดของคุณอย่างละเอียดและใส่ใจกับสแต็กเทรซของคุณการรั่วไหลทั้งหมดไม่ง่ายอย่างที่ปรากฏ
นอกจาก Java VisualVM แล้วยังมีเครื่องมืออื่น ๆ อีกมากมายที่สามารถตรวจจับการรั่วไหลของหน่วยความจำได้ เครื่องตรวจจับการรั่วไหลจำนวนมากทำงานในระดับไลบรารีโดยดักฟังการโทรไปยังรูทีนการจัดการหน่วยความจำ ตัวอย่างเช่น HPROF
เป็นเครื่องมือบรรทัดคำสั่งง่ายๆที่มาพร้อมกับ Java 2 Platform Standard Edition (J2SE) สำหรับการทำโปรไฟล์ฮีปและ CPU ผลลัพธ์ของ HPROF
สามารถวิเคราะห์โดยตรงหรือใช้เป็นอินพุตสำหรับเครื่องมืออื่น ๆ เช่น JHAT
เมื่อเราทำงานกับแอปพลิเคชัน Java 2 Enterprise Edition (J2EE) มีโซลูชันตัววิเคราะห์ฮีปดัมพ์จำนวนมากที่เป็นมิตรกว่าเช่น IBM Heapdumps สำหรับแอ็พพลิเคชันเซิร์ฟเวอร์ Websphere .
หน่วยความจำรั่วคือเมื่อหน่วยความจำถูกทำเครื่องหมายว่า 'ใช้งานอยู่' แม้ว่าจะไม่จำเป็นอีกต่อไปก็ตามและกระบวนการที่สร้างขึ้นจะไม่เป็นอิสระ
aws โซลูชันสถาปนิก สอบสมทบ
ขึ้นอยู่กับการรั่วไหลที่เป็นปัญหา: บางอันสังเกตและแก้ไขได้ง่ายกว่าแบบอื่น ท้ายที่สุดแล้วการไม่ถือเอาการอ้างอิงถึงวัตถุที่ไม่จำเป็นอีกต่อไป บางครั้งสิ่งนี้อยู่ในรหัสของคุณเอง แต่บางครั้งก็อยู่ในไลบรารีที่รหัสของคุณใช้
หากแอปพลิเคชันเพิ่มการใช้หน่วยความจำอย่างต่อเนื่องในขณะที่ประมวลผลข้อมูลประเภทเดียวกันคุณอาจมีหน่วยความจำรั่ว คุณสามารถช่วย จำกัด แหล่งที่มาของการรั่วไหลได้โดยการวิเคราะห์ร่องรอย การมีอยู่ของอินสแตนซ์อ็อบเจ็กต์จำนวนมากที่ควรมีอยู่ในปริมาณเล็กน้อยเท่านั้นโดยทั่วไปบ่งชี้ว่าหน่วยความจำรั่ว
ขนาดฮีปกำหนดจำนวนหน่วยความจำที่พร้อมใช้งานสำหรับ JVM สำหรับการจัดสรร เมื่อใดก็ตามที่คุณสร้างออบเจ็กต์ใหม่วัตถุนั้นจะถูกเก็บไว้ในฮีปอย่างต่อเนื่อง
JVM ใช้วิธีการรวบรวมขยะที่แตกต่างกันสำหรับกลุ่มข้อมูลต่างๆ ข้อมูลจำแนกตามระยะเวลาที่มักจะค้างก่อนที่จะถูกทิ้ง สำหรับข้อมูลระยะสั้นจะหยุดทุกอย่างชั่วคราวและใช้ตัวรวบรวมการคัดลอกการติดตามเพื่อเพิ่มหน่วยความจำที่ไม่ได้อ้างอิงอีกต่อไป
การแก้ไขการรั่วไหลของหน่วยความจำใน Java เกี่ยวข้องกับการสังเกตอาการโดยใช้ verbose GC และการทำโปรไฟล์และวิเคราะห์การติดตามหน่วยความจำตามด้วยการตรวจสอบโค้ดอย่างละเอียดที่ใช้ประโยชน์จากวัตถุที่เกี่ยวข้องกับการรั่วไหล