บทความนี้จะอธิบายหลักการทำงานโดยละเอียดเป็นหลักโดยการวิเคราะห์สแต็ก ฮีป และพูลคงที่ของการจัดสรรหน่วยความจำ Java
1. ต้นแบบหน่วยความจำเครื่องเสมือน Java
ลงทะเบียน: เราไม่สามารถควบคุมโปรแกรมได้ Stack: เก็บข้อมูลประเภทพื้นฐานและการอ้างอิงออบเจ็กต์ แต่ตัวออบเจ็กต์ไม่ได้ถูกเก็บไว้ในสแต็ก แต่ถูกเก็บไว้ในฮีป: เก็บข้อมูลที่สร้างขึ้นด้วยโดเมนแบบคงที่ : ถูกเก็บไว้ในวัตถุ พูลคงที่สมาชิกคงที่กำหนดด้วยคงที่: เก็บค่าคงที่ที่เก็บข้อมูลที่ไม่ใช่ RAM: พื้นที่เก็บข้อมูลถาวรเช่นฮาร์ดดิสก์
2. สระน้ำคงที่
พูลคงที่หมายถึงพูลคงที่ซึ่งถูกกำหนด ณ เวลาคอมไพล์และบันทึกไว้ในหน่วยความจำที่คอมไพล์แล้ว ข้อมูลบางส่วนในไฟล์คลาส นอกเหนือจากค่าคงที่ (สุดท้าย) ที่มีประเภทพื้นฐานต่างๆ (เช่น int, long เป็นต้น) และประเภทอ็อบเจ็กต์ (เช่น สตริงและอาร์เรย์) ที่กำหนดไว้ในโค้ดแล้ว ยังมีการอ้างอิงเชิงสัญลักษณ์ในรูปแบบข้อความอีกด้วย , เช่น:
1. ชื่อคลาสและอินเทอร์เฟซแบบเต็ม
2. ชื่อฟิลด์และคำอธิบาย
3. วิธีการ ชื่อ และคำอธิบาย
เครื่องเสมือนต้องรักษาพูลคงที่สำหรับประเภทที่โหลดแต่ละประเภท พูลค่าคงที่คือชุดลำดับของค่าคงที่ที่ใช้โดยประเภทนี้ รวมถึงค่าคงที่โดยตรง (ค่าคงที่สตริง จำนวนเต็ม และจุดลอยตัว) และการอ้างอิงเชิงสัญลักษณ์ไปยังประเภท ฟิลด์ และวิธีการอื่นๆ สำหรับค่าคงที่สตริง ค่าจะอยู่ในพูลค่าคงที่ พูลคงที่ใน JVM มีอยู่ในรูปแบบของตารางในหน่วยความจำ สำหรับประเภท String จะมีตาราง CONSTANT_String_info ที่มีความยาวคงที่ซึ่งใช้เพื่อจัดเก็บค่าสตริงตามตัวอักษร หมายเหตุ: ตารางนี้เก็บเฉพาะค่าสตริงตามตัวอักษร ไม่ใช่สัญลักษณ์ . เมื่อกล่าวเช่นนี้ คุณควรมีความเข้าใจที่ชัดเจนเกี่ยวกับตำแหน่งการจัดเก็บค่าสตริงในพูลคงที่ เมื่อโปรแกรมถูกดำเนินการ พูลคงที่จะถูกจัดเก็บไว้ในพื้นที่วิธีการแทนฮีป
3. สแต็กในการจัดสรรหน่วยความจำ Java
หน่วยพื้นฐานของสแต็กคือเฟรม (หรือเฟรมสแต็ก): เมื่อใดก็ตามที่เธรด Java ทำงาน เครื่องเสมือน Java จะจัดสรร Java สแต็กให้กับเธรด เมื่อเธรดดำเนินการกับวิธีการ Java บางอย่าง มันจะผลักเฟรมเข้าไปในสแต็ก Java เฟรมนี้ใช้เพื่อจัดเก็บพารามิเตอร์ ตัวแปรโลคัล ตัวถูกดำเนินการ ผลการดำเนินการระดับกลาง ฯลฯ เมื่อวิธีนี้เสร็จสิ้นการดำเนินการ เฟรมจะถูกดึงออกจากสแต็ก ข้อมูลทั้งหมดบน Java Stack เป็นแบบส่วนตัว และไม่มีเธรดอื่นใดที่สามารถเข้าถึงข้อมูลสแต็กของเธรดได้ ข้อมูลตัวแปรพื้นฐานบางประเภทและตัวแปรอ้างอิงอ็อบเจ็กต์ที่กำหนดในฟังก์ชันจะได้รับการจัดสรรในหน่วยความจำสแต็กของฟังก์ชัน เมื่อมีการกำหนดตัวแปรในบล็อกของโค้ด Java จะจัดสรรพื้นที่หน่วยความจำสำหรับตัวแปรบนสแต็ก เมื่อตัวแปรออกจากขอบเขต Java จะปล่อยพื้นที่หน่วยความจำที่จัดสรรให้กับตัวแปรโดยอัตโนมัติ และพื้นที่หน่วยความจำจะสามารถใช้งานได้ทันที ใช้เพื่อวัตถุประสงค์อื่น
4. ฮีปในการจัดสรรหน่วยความจำ Java
ฮีปในเครื่องเสมือน Java ใช้เพื่อจัดเก็บอ็อบเจ็กต์และอาร์เรย์ที่สร้างขึ้นโดยใหม่ หน่วยความจำที่จัดสรรในฮีปได้รับการจัดการโดยกลไกการรวบรวมขยะอัตโนมัติของเครื่องเสมือน Java พูดง่าย ๆ เมื่อเปรียบเทียบกับสแต็ก ฮีปส่วนใหญ่จะใช้เพื่อจัดเก็บอ็อบเจ็กต์ Java และสแต็กส่วนใหญ่จะใช้เพื่อจัดเก็บการอ้างอิงอ็อบเจ็กต์... หลังจากสร้างอาร์เรย์หรืออ็อบเจ็กต์ในฮีปแล้ว ตัวแปรพิเศษยังสามารถเป็น กำหนดไว้ในสแต็ก เพื่อให้ค่าของตัวแปรนี้ในสแต็กเท่ากับที่อยู่แรกของอาร์เรย์หรือวัตถุในหน่วยความจำฮีป ตัวแปรในสแต็กนี้จะกลายเป็นตัวแปรอ้างอิงของอาร์เรย์หรือวัตถุ ตัวแปรอ้างอิงเทียบเท่ากับการตั้งชื่อให้กับอาร์เรย์หรือวัตถุ จากนั้นคุณสามารถใช้ตัวแปรอ้างอิงในสแต็กเพื่อเข้าถึงอาร์เรย์หรือวัตถุในฮีปในโปรแกรมได้ ตัวแปรอ้างอิงเทียบเท่ากับการตั้งชื่อให้กับอาร์เรย์หรือวัตถุ
ตัวแปรอ้างอิงคือตัวแปรธรรมดาที่ได้รับการจัดสรรบนสแต็กเมื่อกำหนด ตัวแปรอ้างอิงจะถูกปล่อยออกมาหลังจากที่โปรแกรมรันนอกขอบเขต อาร์เรย์และอ็อบเจ็กต์นั้นถูกจัดสรรไว้ในฮีป แม้ว่าโปรแกรมจะทำงานนอกบล็อกโค้ดซึ่งมีคำสั่ง new เพื่อสร้างอาร์เรย์หรืออ็อบเจ็กต์อยู่ หน่วยความจำที่ครอบครองโดยอาเรย์และอ็อบเจ็กต์นั้นจะไม่ถูกปล่อยออกมา วัตถุไม่มีตัวแปรอ้างอิงที่ชี้ไป มันกลายเป็นขยะและไม่สามารถใช้งานได้อีกต่อไป แต่ยังคงใช้พื้นที่หน่วยความจำและจะถูกรวบรวม (ปล่อย) โดยตัวรวบรวมขยะในเวลาที่ไม่แน่นอนในภายหลัง นี่เป็นเหตุผลว่าทำไม Java จึงใช้หน่วยความจำมากขึ้น ที่จริงแล้ว ตัวแปรในสแต็กชี้ไปที่ตัวแปรในหน่วยความจำฮีป นี่คือพอยน์เตอร์ใน Java!
ฮีปของ Java คือพื้นที่ข้อมูลรันไทม์ที่คลาสอ็อบเจ็กต์จัดสรรพื้นที่ อ็อบเจ็กต์เหล่านี้ถูกสร้างขึ้นผ่านคำสั่ง เช่น new, newaray, anewarray และ multianewarray และไม่ต้องการโค้ดโปรแกรมที่จะเผยแพร่อย่างชัดเจน ฮีปถูกรวบรวมโดยการรวบรวมขยะ ข้อดีของฮีปคือสามารถจัดสรรขนาดหน่วยความจำแบบไดนามิก และไม่จำเป็นต้องบอกอายุการใช้งานให้กับคอมไพเลอร์ล่วงหน้า เนื่องจากฮีปจะจัดสรรหน่วยความจำแบบไดนามิกขณะรันไทม์ และตัวรวบรวมขยะของ Java จะรวบรวมสิ่งเหล่านี้ที่ไม่ได้ใช้อีกต่อไปโดยอัตโนมัติ ข้อมูล แต่ข้อเสียคือเนื่องจากจำเป็นต้องจัดสรรหน่วยความจำแบบไดนามิกในขณะรันไทม์ ความเร็วในการเข้าถึงจึงช้า
ข้อดีของสแต็กคือความเร็วในการเข้าถึงเร็วกว่าฮีป เป็นรองจากรีจิสเตอร์เท่านั้น และสามารถแชร์ข้อมูลสแต็กได้ แต่ข้อเสียคือต้องกำหนดขนาดและอายุการใช้งานของข้อมูลที่จัดเก็บไว้ในสแต็กและไม่มีความยืดหยุ่น สแต็กส่วนใหญ่จะเก็บข้อมูลตัวแปรพื้นฐานบางประเภท (int, short, long, byte, float, double, boolean, char) และตัวจัดการอ็อบเจ็กต์ (ข้อมูลอ้างอิง)
คุณสมบัติพิเศษที่สำคัญมากของสแต็กคือสามารถแชร์ข้อมูลที่จัดเก็บไว้ในสแต็กได้ สมมติว่าเรายังกำหนด:
int a=3; int b=3; ขั้นแรกคอมไพเลอร์จะประมวลผล int a = 3; ขั้นแรกให้สร้างการอ้างอิงถึงตัวแปร a บนสแต็ก จากนั้นตรวจสอบว่ามีค่า 3 บนสแต็กหรือไม่ มันจะตั้งค่า 3 เก็บไว้ในนั้นแล้วชี้ a ไปที่ 3 จากนั้นประมวลผล int b = 3; หลังจากสร้างตัวแปรอ้างอิงของ b แล้ว เนื่องจากมีค่า 3 บนสแต็กอยู่แล้ว b จะถูกชี้ไปที่ 3 โดยตรง ด้วยวิธีนี้ a และ b จะปรากฏขึ้น ในเวลาเดียวกัน ทั้งสองชี้ไปที่กรณีของ 3
ในเวลานี้ หากตั้งค่า a=4 อีกครั้ง คอมไพเลอร์จะค้นหาอีกครั้งว่ามีค่า 4 ในสแต็กหรือไม่ หากไม่เป็นเช่นนั้น จะเก็บค่า 4 ไว้และชี้ไปที่ 4 ถ้ามีอยู่แล้ว จะชี้ a ไปยังที่อยู่นี้โดยตรง ดังนั้นการเปลี่ยนแปลงค่า a จะไม่ส่งผลต่อค่า b
ควรสังเกตว่าการแบ่งปันข้อมูลประเภทนี้แตกต่างจากการแบ่งปันการอ้างอิงวัตถุสองวัตถุที่ชี้ไปยังวัตถุเดียวในเวลาเดียวกัน เพราะในกรณีนี้การแก้ไข a จะไม่ส่งผลกระทบต่อ b คอมไพเลอร์จะเสร็จสมบูรณ์ซึ่ง มีประโยชน์ในการประหยัดพื้นที่ หากตัวแปรอ้างอิงออบเจ็กต์แก้ไขสถานะภายในของออบเจ็กต์ จะส่งผลต่อตัวแปรอ้างอิงออบเจ็กต์อื่น