ทำความเข้าใจกับหน่วยความจำใน JVM
ดังที่คุณทราบแล้วว่า JVM รันโปรแกรม Java ภายในตัวมันเอง เช่นเดียวกับเครื่องเสมือนอื่น ๆ มีระบบการจัดการหน่วยความจำของตัวเอง
เค้าโครงหน่วยความจำภายในระบุว่าแอปพลิเคชัน Java ของคุณทำงานอย่างไร ด้วยวิธีนี้ทำให้สามารถระบุคอขวดในการทำงานของแอปพลิเคชันและอัลกอริทึมได้ เรามาดูกันว่ามันทำงานอย่างไร
![ทำความเข้าใจกับหน่วยความจำใน JVM](https://cdn.codegym.cc/images/article/1180008a-b863-4007-b14b-10c0a106ecb8/800.jpeg)
สำคัญ! โมเดล Java ดั้งเดิมนั้นไม่ดีพอ ดังนั้นจึงมีการแก้ไขใน Java 1.5 เวอร์ชันนี้ใช้มาจนถึงทุกวันนี้ (Java 14+)
กองกระทู้
โมเดลหน่วยความจำ Java ที่ใช้ภายในโดย JVM แบ่งหน่วยความจำออกเป็นเธรดสแต็กและฮีป มาดูโมเดลหน่วยความจำ Java ซึ่งแบ่งออกเป็นบล็อกอย่างมีเหตุผล:
![กองกระทู้](https://cdn.codegym.cc/images/article/06c9cf70-8f47-463d-b6bd-e19ae1f5d72a/512.jpeg)
เธรดทั้งหมดที่ทำงานใน JVM มีสแต็กของตัวเอง ในทางกลับกันสแต็กจะเก็บข้อมูลเกี่ยวกับเมธอดที่เธรดเรียกใช้ ฉันจะเรียกสิ่งนี้ว่า "คอลสแตก" call stack ดำเนินการต่อทันทีที่เธรดรันโค้ด
สแต็กของเธรดประกอบด้วยตัวแปรโลคัลทั้งหมดที่จำเป็นสำหรับการดำเนินการเมธอดบนสแต็กของเธรด เธรดสามารถเข้าถึงสแต็กของตัวเองเท่านั้น เธรดอื่นไม่สามารถมองเห็นตัวแปรโลคัลได้ เฉพาะกับเธรดที่สร้างตัวแปรเหล่านั้น ในสถานการณ์ที่เธรดสองเธรดรันโค้ดเดียวกัน เธรดทั้งสองจะสร้างตัวแปรโลคัลของตัวเอง ดังนั้น แต่ละเธรดจึงมีเวอร์ชันของตัวเองสำหรับตัวแปรโลคัลแต่ละตัว
ตัวแปรโลคัลประเภทดั้งเดิมทั้งหมด ( บูลีน , ไบต์ , สั้น , ถ่าน , int , ยาว , float , double ) จะถูกจัดเก็บไว้ในเธรดสแต็กทั้งหมดและไม่สามารถมองเห็นได้ในเธรดอื่น หนึ่งเธรดสามารถส่งสำเนาของตัวแปรดั้งเดิมไปยังเธรดอื่นได้ แต่ไม่สามารถแชร์ตัวแปรโลคัลดั้งเดิมได้
กอง
ฮีปประกอบด้วยวัตถุทั้งหมดที่สร้างขึ้นในแอปพลิเคชันของคุณ โดยไม่คำนึงว่าเธรดใดที่สร้างวัตถุนั้น ซึ่งรวมถึงการห่อหุ้มประเภทดั้งเดิม (เช่นByte , Integer , Longและอื่น ๆ ) ไม่สำคัญว่าออบเจ็กต์จะถูกสร้างขึ้นและกำหนดให้กับตัวแปรโลคัลหรือสร้างเป็นตัวแปรสมาชิกของออบเจ็กต์อื่น อ็อบเจ็กต์จะถูกเก็บไว้ในฮีป
ด้านล่างนี้เป็นไดอะแกรมที่แสดง call stack และตัวแปรโลคอล (เก็บไว้ใน stack) รวมถึงอ็อบเจกต์ (เก็บไว้ใน heap):
![กอง](https://cdn.codegym.cc/images/article/7b8ec0e5-49eb-4a16-b473-7462cbbc4065/800.jpeg)
ในกรณีที่ตัวแปรโลคัลเป็นประเภทดั้งเดิม ตัวแปรนั้นจะถูกเก็บไว้ในสแต็กของเธรด
ตัวแปรโลคัลยังสามารถอ้างอิงถึงวัตถุ ในกรณีนี้ การอ้างอิง (ตัวแปรโลคัล) จะถูกจัดเก็บไว้ในเธรดสแต็ก แต่ตัวออบเจกต์นั้นถูกจัดเก็บไว้ในฮีป
วัตถุมีเมธอด เมธอดเหล่านี้มีตัวแปรโลคัล ตัวแปรโลคัลเหล่านี้ยังถูกจัดเก็บไว้ในเธรดสแต็ก แม้ว่าอ็อบเจ็กต์ที่เป็นเจ้าของเมธอดจะถูกเก็บไว้ในฮีปก็ตาม
ตัวแปรสมาชิกของอ็อบเจกต์จะถูกเก็บไว้ในฮีปพร้อมกับตัวอ็อบเจ็กต์เอง สิ่งนี้เป็นจริงทั้งเมื่อตัวแปรสมาชิกเป็นประเภทดั้งเดิมและเมื่อเป็นการอ้างอิงวัตถุ
ตัวแปรคลาสคงที่จะถูกเก็บไว้ในฮีปพร้อมกับคำจำกัดความของคลาส
ปฏิสัมพันธ์กับวัตถุ
วัตถุบนฮีปสามารถเข้าถึงได้โดยเธรดทั้งหมดที่มีการอ้างอิงถึงวัตถุ หากเธรดสามารถเข้าถึงวัตถุได้ ก็จะสามารถเข้าถึงตัวแปรของวัตถุได้ หากเธรดสองเธรดเรียกเมธอดบนวัตถุเดียวกันในเวลาเดียวกัน เธรดทั้งสองจะมีสิทธิ์เข้าถึงตัวแปรสมาชิกของวัตถุ แต่แต่ละเธรดจะมีสำเนาของตัวแปรโลคัลของตัวเอง
![การโต้ตอบกับวัตถุ (ฮีป)](https://cdn.codegym.cc/images/article/130d32a3-c025-4f2b-9a40-616ba1fd2080/800.jpeg)
สองเธรดมีชุดของตัวแปรโลคัลตัวแปรท้องถิ่น2ชี้ไปที่วัตถุที่ใช้ร่วมกันบนฮีป (วัตถุ 3). แต่ละเธรดมีสำเนาของตัวแปรโลคัลของตัวเองพร้อมการอ้างอิงของตัวเอง การอ้างอิงเป็นตัวแปรในเครื่อง ดังนั้นจึงถูกจัดเก็บไว้ในเธรดสแต็ก อย่างไรก็ตาม การอ้างอิงที่แตกต่างกันสองรายการชี้ไปที่วัตถุเดียวกันบนฮีป
โปรดทราบว่าทั่วไปวัตถุ 3มีลิงค์ไปยังวัตถุ 2และวัตถุ 4เป็นตัวแปรสมาชิก (แสดงด้วยลูกศร) ผ่านลิงก์เหล่านี้ สามารถเข้าถึงสองเธรดได้วัตถุ 2และวัตถุ4.
ไดอะแกรมยังแสดงตัวแปรโลคัล (ตัวแปรท้องถิ่น 1จากmethodTwo ) แต่ละสำเนามีการอ้างอิงที่แตกต่างกันซึ่งชี้ไปยังวัตถุสองชิ้นที่แตกต่างกัน (วัตถุ 1และวัตถุ 5) และไม่ใช่อันเดียวกัน ตามทฤษฎีแล้ว ทั้งสองเธรดสามารถเข้าถึงได้ทั้งสองอย่างวัตถุ 1, เพื่อที่จะวัตถุ 5หากมีการอ้างอิงถึงวัตถุทั้งสองนี้ แต่ในแผนภาพด้านบน แต่ละเธรดมีการอ้างอิงถึงหนึ่งในสองวัตถุเท่านั้น
ตัวอย่างของการโต้ตอบกับวัตถุ
มาดูกันว่าเราจะสาธิตการทำงานในโค้ดได้อย่างไร:
public class MySomeRunnable implements Runnable() {
public void run() {
one();
}
public void one() {
int localOne = 1;
Shared localTwo = Shared.instance;
//... do something with local variables
two();
}
public void two() {
Integer localOne = 2;
//... do something with local variables
}
}
public class Shared {
// store an instance of our object in a variable
public static final Shared instance = new Shared();
// member variables pointing to two objects on the heap
public Integer object2 = new Integer(22);
public Integer object4 = new Integer(44);
}
เมธอดrun()เรียก เมธอด one()และone() เรียก two()ในทางกลับกัน
เมธอดone()ประกาศตัวแปรท้องถิ่นดั้งเดิม (localOne) ของประเภทintและตัวแปรท้องถิ่น (localTwo) ซึ่งเป็นการอ้างอิงถึงวัตถุ
แต่ละเธรดที่ดำเนินการเมธอดone()จะสร้างสำเนาของตัวเองlocalOneและlocalTwoในกองของคุณ ตัวแปรlocalOneจะถูกแยกออกจากกันโดยสมบูรณ์อยู่บนกองด้ายแต่ละเส้น เธรดหนึ่งไม่สามารถเห็นได้ว่าเธรดอื่นเปลี่ยนแปลงอะไรในการคัดลอกlocalOne.
แต่ละเธรดที่ดำเนินการเมธอดone()จะสร้างสำเนาของตัวเองด้วยlocalTwo. อย่างไรก็ตาม สำเนาสองฉบับที่แตกต่างกันlocalTwoจบลงด้วยการชี้ไปที่วัตถุเดียวกันบนฮีป ความจริงก็คือว่าlocalTwoชี้ไปที่วัตถุที่อ้างอิงโดยตัวแปรคงที่ตัวอย่าง. มีสำเนาของตัวแปรสแตติกเพียงสำเนาเดียว และสำเนานั้นจะถูกเก็บไว้ในฮีป
ดังนั้นสำเนาทั้งสองlocalTwoจบลงด้วยการชี้ไปที่ อินส แตน ซ์ ที่ใช้ร่วม กันเดียวกัน อินสแตนซ์ที่ใช้ร่วมกันจะถูกเก็บไว้ในฮีปด้วย มันเข้ากันวัตถุ 3ในแผนภาพด้านบน
โปรดทราบว่า คลาส ที่ใช้ร่วมกันยังมีตัวแปรสมาชิกสองตัว ตัวแปรสมาชิกจะถูกเก็บไว้ในกองพร้อมกับวัตถุ ตัวแปรสมาชิกสองตัวชี้ไปที่วัตถุอื่นอีกสองตัวจำนวนเต็ม. วัตถุจำนวนเต็มเหล่านี้สอดคล้องกับวัตถุ 2และวัตถุ 4บนแผนภาพ
โปรดทราบว่า เมธอด two()สร้างตัวแปรโลคัลชื่อlocalOne. ตัวแปรโลคัล นี้เป็นการอ้างอิงถึงออบเจกต์ประเภทInteger วิธีการกำหนดลิงค์localOneเพื่อชี้ไปที่อินสแตน ซ์จำนวนเต็ม ใหม่ ลิงค์จะถูกเก็บไว้ในสำเนาlocalOneสำหรับแต่ละเธรด อินสแตน ซ์จำนวนเต็ม สอง อินสแตนซ์ จะถูกเก็บไว้ในฮีป และเนื่องจากเมธอดนี้สร้างออบเจกต์จำนวนเต็มใหม่ทุกครั้งที่ดำเนินการ เธรดสองเธรดที่ดำเนินการเมธอดนี้จะสร้างอินส แตนซ์จำนวนเต็มแยกกัน พวกเขาตรงกันวัตถุ 1และวัตถุ 5ในแผนภาพด้านบน
สังเกตตัวแปรสมาชิกสองตัวใน คลาส ที่ใช้ร่วมกันประเภทIntegerซึ่งเป็นประเภทดั้งเดิม เนื่องจากตัวแปรเหล่านี้เป็นตัวแปรสมาชิก จึงยังคงถูกจัดเก็บไว้ในฮีปพร้อมกับวัตถุ เฉพาะตัวแปรโลคัลเท่านั้นที่เก็บไว้บนเธรดสแต็ก