JVM จัดการหน่วยความจำสองประเภท ได้แก่ ฮีปและไม่ใช่ฮีป ฮีปมีไว้สำหรับนักพัฒนาเพื่อใช้ตามที่กล่าวไว้ข้างต้น มันถูกสร้างขึ้นเมื่อ JVM เริ่มทำงาน ส่วนที่ไม่ใช่ฮีปจะถูกสงวนไว้สำหรับ JVM เองเพื่อเก็บข้อมูลคลาส มันแตกต่างจากฮีป GC จะไม่ปล่อยพื้นที่ระหว่างรันไทม์
1. ประเภทหน่วยความจำล้น
1. java.lang.OutOfMemoryError: พื้นที่ PermGen
JVM จัดการหน่วยความจำสองประเภท ได้แก่ ฮีปและไม่ใช่ฮีป ฮีปมีไว้สำหรับนักพัฒนาเพื่อใช้ตามที่กล่าวไว้ข้างต้น มันถูกสร้างขึ้นเมื่อ JVM เริ่มทำงาน ส่วนที่ไม่ใช่ฮีปจะถูกสงวนไว้สำหรับ JVM เองเพื่อเก็บข้อมูลคลาส มันแตกต่างจากฮีป GC จะไม่ปล่อยพื้นที่ระหว่างรันไทม์ หากเว็บแอปใช้ขวดของบุคคลที่สามจำนวนมากหรือแอปพลิเคชันมีไฟล์คลาสมากเกินไปและการตั้งค่า MaxPermSize มีขนาดเล็ก การเกินขีดจำกัดจะทำให้หน่วยความจำใช้มากเกินไปและทำให้เกิดการโอเวอร์โฟลว์ หรือ Tomcat จะ ไม่ทำความสะอาดส่วนหน้าในระหว่างการปรับใช้แบบ hot สภาพแวดล้อมที่โหลดจะเปลี่ยนบริบทไปเป็นบริบทที่เพิ่งปรับใช้ใหม่ และจะมีเนื้อหาที่ไม่ซ้อนกันมากขึ้นเรื่อยๆ
ชื่อเต็มของพื้นที่ PermGen คือพื้นที่ถาวรซึ่งหมายถึงพื้นที่เก็บข้อมูลถาวรของหน่วยความจำ JVM ส่วนใหญ่จะใช้เพื่อเก็บข้อมูลคลาสและคลาส Meta จะถูกวางไว้ในพื้นที่ PermGen โหลดโดย Loader และจัดเก็บคลาสอินสแตนซ์ (อินสแตนซ์) พื้นที่ฮีปแตกต่างกัน GC (Garbage Collection) จะไม่ล้างพื้นที่ PermGen ในระหว่างการทำงานของโปรแกรมหลัก ดังนั้นหากแอปพลิเคชันของคุณมี CLASS จำนวนมาก ข้อผิดพลาดเกี่ยวกับพื้นที่ PermGen มีแนวโน้มที่จะเกิดขึ้น ข้อผิดพลาดประเภทนี้เป็นเรื่องปกติเมื่อเว็บเซิร์ฟเวอร์คอมไพล์ JSP ล่วงหน้า หากเว็บแอปของคุณใช้ขวดของบุคคลที่สามจำนวนมาก และขนาดของขวดนั้นเกินขนาดเริ่มต้นของ jvm (4M) ข้อความแสดงข้อผิดพลาดนี้จะถูกสร้างขึ้น
ตัวอย่างการกำหนดค่าที่ดีที่สุด: (หลังจากตรวจสอบด้วยตัวเองแล้ว ตั้งแต่ใช้การกำหนดค่านี้ Tomcat ก็ไม่เคยตายอีกเลย)
ตั้งค่า JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
ภายใต้ Linux ให้เพิ่มบรรทัดโค้ดตามที่แสดงเป็นสีแดงใน tomcathome/conf/catalina.sh: คุณสามารถเพิ่มหน่วยความจำของ tomcat jvm เพื่อให้หน่วยความจำล้นมีโอกาสเกิดขึ้นน้อยลง!
# ----- ดำเนินการคำสั่งที่ร้องขอ -------------------------------------------- - -
JAVA_OPTS="-server -Xms512m -Xmx2048m -XX:PermSize=128m -XX:MaxNewSize=256m -XX:MaxPermSize=256m"
# Bugzilla 37848: ส่งออกเฉพาะในกรณีที่เรามี TTY
2. java.lang.OutOfMemoryError: พื้นที่ Javaheap
สถานการณ์แรกคืออาหารเสริม และปัญหาหลักเกิดขึ้นในสถานการณ์นี้ พื้นที่เริ่มต้น (เช่น -Xms) คือ 1/64 ของหน่วยความจำกายภาพ และพื้นที่สูงสุด (-Xmx) คือ 1/4 ของหน่วยความจำกายภาพ หากหน่วยความจำเหลือน้อยกว่า 40% JVM จะเพิ่มฮีปเป็นค่าที่กำหนดโดย Xmx หากหน่วยความจำเหลืออยู่เกิน 70% JVM จะลดฮีปเป็นค่าที่กำหนดโดย Xms ดังนั้น โดยทั่วไปการตั้งค่า Xmx และ Xms ของเซิร์ฟเวอร์ควรได้รับการตั้งค่าเหมือนกันเพื่อหลีกเลี่ยงการปรับขนาดฮีปเครื่องเสมือนหลังจากแต่ละ GC สมมติว่าหน่วยความจำกายภาพไม่มีที่สิ้นสุด หน่วยความจำ JVM สูงสุดจะขึ้นอยู่กับระบบปฏิบัติการ โดยทั่วไป เครื่อง 32 บิตจะอยู่ระหว่าง 1.5g ถึง 3g ในขณะที่เครื่อง 64 บิตไม่มีขีดจำกัด
หมายเหตุ: หาก Xms เกินค่า Xmx หรือผลรวมของค่าฮีปสูงสุดและค่าที่ไม่ใช่ฮีปสูงสุดเกินขีดจำกัดสูงสุดของหน่วยความจำกายภาพหรือระบบปฏิบัติการ เซิร์ฟเวอร์จะไม่เริ่มทำงาน
บทบาทการเก็บขยะ GC
ความถี่ของการเรียก JVM GC ยังคงสูงมาก และการรวบรวมขยะจะดำเนินการในสองสถานการณ์หลัก:
เมื่อเธรดแอปพลิเคชันไม่ได้ใช้งาน อีกประการหนึ่งคือเมื่อฮีปหน่วยความจำ Java ไม่เพียงพอ GC จะถูกเรียกอย่างต่อเนื่อง หากการรีไซเคิลอย่างต่อเนื่องไม่สามารถแก้ไขปัญหาฮีปหน่วยความจำไม่เพียงพอ ข้อผิดพลาดของหน่วยความจำไม่เพียงพอจะถูกรายงาน เนื่องจากข้อยกเว้นนี้ขึ้นอยู่กับสภาพแวดล้อมการทำงานของระบบ จึงไม่สามารถคาดการณ์ได้ว่าจะเกิดขึ้นเมื่อใด
ตามกลไก GC การรันโปรแกรมจะทำให้เกิดการเปลี่ยนแปลงสภาพแวดล้อมการทำงานของระบบ เพิ่มโอกาสที่ GC จะทริกเกอร์
เพื่อหลีกเลี่ยงปัญหาเหล่านี้ การออกแบบและการเขียนโปรแกรมควรหลีกเลี่ยงการครอบครองหน่วยความจำของวัตถุขยะและโอเวอร์เฮดของ GC การเรียก System.GC() อย่างชัดเจนสามารถแนะนำได้ว่า JVM จำเป็นต้องรีไซเคิลออบเจ็กต์ขยะในหน่วยความจำ แต่ไม่จำเป็นต้องรีไซเคิลทันที
หนึ่งคือไม่สามารถแก้ปัญหาทรัพยากรหน่วยความจำไม่เพียงพอได้ และยังจะเพิ่มการใช้ GC ด้วย
2. องค์ประกอบของพื้นที่หน่วยความจำ JVM <BR>แค่พูดถึงฮีปและสแต็กใน java
Java แบ่งหน่วยความจำออกเป็นสองประเภท ประเภทหนึ่งคือหน่วยความจำแบบสแต็ก และอีกประเภทหนึ่งคือหน่วยความจำฮีป
1. ตัวแปรประเภทพื้นฐานและตัวแปรอ้างอิงวัตถุที่กำหนดในฟังก์ชันจะได้รับการจัดสรรในหน่วยความจำสแต็กของฟังก์ชัน
2. หน่วยความจำฮีปใช้เพื่อจัดเก็บอ็อบเจ็กต์และอาร์เรย์ที่สร้างโดยใหม่ เมื่อมีการกำหนดตัวแปรในฟังก์ชัน (บล็อกโค้ด) Java จะจัดสรรพื้นที่หน่วยความจำสำหรับตัวแปรบนสแต็ก เมื่อเกินขอบเขตของตัวแปร Java จะ ปล่อยมันโดยอัตโนมัติ ลบพื้นที่หน่วยความจำที่จัดสรรสำหรับตัวแปร หน่วยความจำที่จัดสรรในฮีปได้รับการจัดการโดยตัวรวบรวมขยะอัตโนมัติของเครื่องเสมือน Java ไม่จำเป็นต้องแจ้งให้คอมไพลเลอร์ทราบล่วงหน้า เนื่องจากอยู่ในหน่วยความจำจะได้รับการจัดสรรแบบไดนามิกขณะรันไทม์ ข้อเสียคือต้องจัดสรรหน่วยความจำแบบไดนามิกขณะรันไทม์ และความเร็วในการเข้าถึงช้า
ข้อดีของสแต็กคือความเร็วในการเข้าถึงเร็วกว่าฮีป ข้อเสียคือขนาดและอายุการใช้งานของข้อมูลที่จัดเก็บไว้ในสแต็กจะต้องกำหนดได้เองและไม่ยืดหยุ่น
ฮีป Java แบ่งออกเป็นสามส่วน: ใหม่ เก่า และถาวร
GC มีสองเธรด:
วัตถุที่สร้างขึ้นใหม่จะถูกจัดสรรไปยังพื้นที่ใหม่ เมื่อพื้นที่ถูกเติมเต็ม มันจะถูกย้ายไปยังพื้นที่เก่าโดยเธรดเสริมของ GC เมื่อพื้นที่เก่าถูกเติมเต็มเช่นกัน เธรดหลักของ GC จะถูกทริกเกอร์ให้สำรวจวัตถุทั้งหมดเข้าไป หน่วยความจำฮีป ขนาดของพื้นที่เก่าเท่ากับ Xmx ลบ -Xmn
การปรับสแต็กหน่วยเก็บข้อมูลสแต็ก Java: พารามิเตอร์คือ +UseDefaultStackSize -Xss256K ซึ่งหมายความว่าแต่ละเธรดสามารถใช้กับพื้นที่สแต็ก 256k แต่ละเธรดมีสแต็กของตัวเอง
3. วิธีการตั้งค่าหน่วยความจำเสมือนใน JVM <BR>เคล็ดลับ: ใน JVM หากใช้เวลา 98% สำหรับ GC และขนาดฮีปที่มีอยู่น้อยกว่า 2% ข้อความข้อยกเว้นนี้จะถูกส่งออกไป
เคล็ดลับ: ขนาดฮีปสูงสุดไม่ควรเกิน 80% ของหน่วยความจำฟิสิคัลที่มีอยู่ โดยทั่วไป ตัวเลือก -Xms และ -Xmx ควรตั้งค่าเป็นค่าเดียวกัน และ -Xmn ควรเป็น 1/4 ของค่า -Xmx
เคล็ดลับ: หน่วยความจำเริ่มต้นที่จัดสรรโดย JVM ถูกระบุโดย -Xms และค่าดีฟอลต์คือ 1/64 ของหน่วยความจำฟิสิคัล ส่วนหน่วยความจำสูงสุดที่จัดสรรโดย JVM ถูกระบุโดย -Xmx และค่าดีฟอลต์คือ 1/4 ของฟิสิคัล หน่วยความจำ.
ตามค่าเริ่มต้น เมื่อหน่วยความจำฮีปว่างน้อยกว่า 40% JVM จะเพิ่มฮีปจนถึงขีดจำกัดสูงสุดที่ -Xmx เมื่อหน่วยความจำฮีปว่างมากกว่า 70% JVM จะลดฮีปจนถึงขีดจำกัดขั้นต่ำ -Xms ดังนั้น โดยทั่วไปเซิร์ฟเวอร์จะตั้งค่า -Xms และ -Xmx ให้เท่ากับเพื่อหลีกเลี่ยงการปรับขนาดฮีปหลังจากแต่ละ GC
เคล็ดลับ: สมมติว่าหน่วยความจำฟิสิคัลไม่มีที่สิ้นสุด ค่าสูงสุดของหน่วยความจำ JVM จะมีความสัมพันธ์ที่ดีกับระบบปฏิบัติการ
พูดง่ายๆ ก็คือ แม้ว่าโปรเซสเซอร์ 32 บิตจะมีพื้นที่หน่วยความจำที่ควบคุมได้ที่ 4GB แต่ระบบปฏิบัติการเฉพาะจะกำหนดขีดจำกัด
โดยทั่วไปขีดจำกัดนี้คือ 2GB-3GB (โดยทั่วไปคือ 1.5G-2G สำหรับระบบ Windows และ 2G-3G สำหรับระบบ Linux) และจะไม่มีขีดจำกัดสำหรับโปรเซสเซอร์ที่สูงกว่า 64 บิต
หมายเหตุ: หาก Xms เกินค่า Xmx หรือผลรวมของค่าฮีปสูงสุดและค่าที่ไม่ใช่ฮีปสูงสุดเกินขีดจำกัดสูงสุดของหน่วยความจำกายภาพหรือระบบปฏิบัติการ เซิร์ฟเวอร์จะไม่เริ่มทำงาน
เคล็ดลับ: ตั้งค่า NewSize และ MaxNewSize ให้เท่ากัน และขนาดของ "ใหม่" ไม่ควรใหญ่กว่าครึ่งหนึ่งของ "เก่า" เหตุผลก็คือ หากพื้นที่เก่าไม่ใหญ่พอ GC "หลัก" จะถูกทริกเกอร์บ่อยครั้ง ซึ่งลดประสิทธิภาพลงอย่างมาก
JVM ใช้ -XX:PermSize เพื่อตั้งค่าเริ่มต้นของหน่วยความจำที่ไม่ใช่ฮีป ค่าเริ่มต้นคือ 1/64 ของหน่วยความจำกายภาพ
ขนาดหน่วยความจำสูงสุดที่ไม่ใช่ฮีปถูกกำหนดโดย XX:MaxPermSize ค่าเริ่มต้นคือ 1/4 ของหน่วยความจำกายภาพ
วิธีแก้ไข: ตั้งค่าขนาดฮีปด้วยตนเอง
แก้ไข TOMCAT_HOME/bin/catalina.bat
เพิ่มบรรทัดต่อไปนี้เหนือ "echo "การใช้ CATALINA_BASE: $CATALINA_BASE"":
JAVA_OPTS="-เซิร์ฟเวอร์ -Xms800m -Xmx800m -XX:MaxNewSize=256m"
4. ใช้เครื่องมือตรวจสอบประสิทธิภาพ เพื่อค้นหาหน่วยความจำรั่ว:
เครื่องมือ JProfiler ส่วนใหญ่จะใช้ในการตรวจสอบและติดตามประสิทธิภาพของระบบ (จำกัด เฉพาะการพัฒนา Java) JProfiler สามารถตรวจสอบการทำงานและประสิทธิภาพของ JVM ได้โดยการตรวจสอบการใช้งานหน่วยความจำของระบบ การรวบรวมขยะ สถานะการทำงานของเธรด และวิธีการอื่น ๆ อย่างต่อเนื่องตลอดเวลา
1. หน่วยความจำของแอปพลิเคชันเซิร์ฟเวอร์ถูกครอบครองอย่างไม่สมเหตุสมผลเป็นเวลานาน หน่วยความจำมักถูกครอบครองในระดับสูงและยากต่อการกู้คืนสู่ระดับต่ำ