JVM ไม่อยู่ในหน่วยความจำสาเหตุ

Aug 20 2020

คำถามอ้างอิงจาก Oracle Hotspot JDK8

เมื่อแอปพลิเคชันพบjava.lang.OutOfMemory: Java heap spaceข้อยกเว้นฉันคิดว่ามีเหตุผลสองประการที่เป็นไปได้

  1. ขนาดฮีป JVM ที่จัดสรรถึงขนาดที่-Xmxกำหนดและระบบ GC ไม่สามารถบีบพื้นที่ได้เพียงพอ
  2. ฮีป JVM ที่จัดสรรไม่สามารถเข้าถึงได้-Xmxแต่มีหน่วยความจำกายภาพไม่เพียงพอสำหรับฮีป JVM ที่จะเติบโต สมมติว่า-Xms< -Xmx.

ฉันรู้ว่า1เป็นเหตุผลให้ JVM ยกเลิกjava.lang.OutOfMemory: Java heap spaceข้อยกเว้น เป็น2เหตุผลหนึ่งสาเหตุ?

ฉันพบบางบทความที่กล่าวถึงjava.lang.OutOfMemoryError: native memory exhaustedแต่ทั้งหมดนี้ จำกัด อยู่ที่เว็บไซต์ IBM Expetion นี้ จำกัด เฉพาะ IBM ที่ใช้ JVM หรือเป็นค่า expetion มาตรฐานในข้อกำหนด JVM?


ฉันทำการทดลองบางอย่างกับโค้ดที่ @Eugene ให้มาในคำตอบ ดังที่ @Holger ตั้งข้อสังเกตว่าผลลัพธ์แตกต่างกันไปในสภาพแวดล้อมที่แตกต่างกัน ฉันทดสอบทั้งบน CentOS x64 และ Win7 x64 ด้วย Hotspot JDK8 x64 เพื่อความเรียบง่ายการสลับและหน่วยความจำเสมือนถูกปิดใช้งาน

ฉันเพิ่มขอบเขตหน่วยความจำ (-Xmx และ -Xms) ทีละขั้นตอน

I. -Xmx < หน่วยความจำลอจิกที่ใช้ได้

  • ทั้งบน CentOS และ Windows จะแสดงOutOfMemoryError: Java heap space

II. หน่วยความจำลอจิกที่ใช้ได้ <-Xmx < หน่วยความจำกายภาพสูงสุด

  • CentOS: GC พยายามใช้เวลาให้บริการ GC เต็มรูปแบบด้วยการจัดสรรล้มเหลวและกระบวนการจะถูกฆ่าโดยระบบโดยทิ้งข้อความไว้ว่าถูกฆ่า
  • Windows: GC พยายามใช้เวลาให้บริการ GC เต็มรูปแบบพร้อมด้วยAllocation Failureและทิ้งOutOfMemoryError: Java heap space

สาม. -Xmx> หน่วยความจำกายภาพสูงสุด

  • CentOS: เหมือนกับใน II
  • Windows: เหมือนกับใน II

IV. -Xms> หน่วยความจำกายภาพสูงสุด

  • CentOS: JVM ดูเหมือนจะไม่สามารถเริ่มต้นได้ ข้อความแสดงข้อผิดพลาดเป็นดังนี้:

คำเตือน Java HotSpot (TM) 64-Bit Server VM: INFO: os :: commit_memory (0x00000000e62a0000, 349569024, 0) ล้มเหลว ข้อผิดพลาด = 'ไม่สามารถจัดสรรหน่วยความจำได้' (errno = 12)

  • Windows: JVM ไม่สามารถเริ่มทำงานได้ ข้อความแสดงข้อผิดพลาดเป็นดังนี้:

เกิดข้อผิดพลาดระหว่างการเริ่มต้น VM ไม่สามารถจองพื้นที่เพียงพอสำหรับฮีปวัตถุ


ดังนั้น JVM เดียวกันจึงทำงานแตกต่างกันใน OS ที่แตกต่างกัน

  • บน windows ระบบปฏิบัติการจะไม่ฆ่า JVM และ JVM จะโยนOutOfMemoryError: Java heap space เสมอเมื่อการใช้งานหน่วยความจำเพิ่มขึ้น
  • บน Linux ระบบปฏิบัติการจะฆ่าเมื่อมีหน่วยความจำไม่เพียงพอ
  • บน OS JVM ทั้งสองล้มเหลวในการเริ่มทำงานเมื่อหน่วยความจำที่มีอยู่ไม่เป็นไปตามข้อกำหนดขั้นต่ำของ JVM

คำตอบ

1 Eugene Aug 20 2020 at 18:04

ประการแรกมีสาเหตุมากกว่าที่ GC จะล้มเหลวโดย "หน่วยความจำไม่เพียงพอ" ตามที่ความคิดเห็นในคำถามของคุณอธิบาย

การพิสูจน์หมายเลขจุด (2) เป็นเรื่องง่ายเพียงสร้างรหัสที่จัดสรรไว้เสมอ:

public static void main(String[] args) {
    test(1);
}

static void test(int x){
    List<byte[]> list = new ArrayList<>();
    while(x == 1){
        byte [] b =new byte[1 * 1024 * 1024];
        b[100] = 42;
        list.add(b);
    }

    System.out.println(list.hashCode());
}

และรันสิ่งนี้ด้วย-Xms1g -Xmx100gระบบที่มี100gRAM น้อยกว่า คุณสามารถเปิดใช้งานบันทึก GC (เช่นฉันทำ"-Xlog:heap*=debug" "-Xlog:gc*=debug"ในแฟล็ก java-9) และดูว่า GC พยายามจัดการกับการจัดสรรคงที่นี้ยากเพียงใดในที่สุดก็ล้มเหลว