โค้ดยิม/หลักสูตรจาวา/โมดูล 3/หน่วยความจำใน JVM ตอนที่ 2

หน่วยความจำใน JVM ตอนที่ 2

ระดับ, บทเรียน
มีอยู่

สถาปัตยกรรมฮาร์ดแวร์หน่วยความจำ

สถาปัตยกรรมฮาร์ดแวร์หน่วยความจำสมัยใหม่แตกต่างจากโมเดลหน่วยความจำภายในของ Java ดังนั้น คุณต้องเข้าใจสถาปัตยกรรมฮาร์ดแวร์เพื่อที่จะรู้ว่าโมเดล Java นั้นทำงานอย่างไร ส่วนนี้อธิบายถึงสถาปัตยกรรมฮาร์ดแวร์หน่วยความจำทั่วไป และส่วนถัดไปจะอธิบายวิธีที่ Java ทำงานร่วมกับมัน

นี่คือไดอะแกรมอย่างง่ายของสถาปัตยกรรมฮาร์ดแวร์ของคอมพิวเตอร์สมัยใหม่:

สถาปัตยกรรมฮาร์ดแวร์หน่วยความจำ

ในโลกสมัยใหม่ คอมพิวเตอร์มีโปรเซสเซอร์ตั้งแต่ 2 ตัวขึ้นไป และนี่เป็นบรรทัดฐานอยู่แล้ว โปรเซสเซอร์เหล่านี้บางตัวอาจมีหลายคอร์ บนคอมพิวเตอร์ดังกล่าว เป็นไปได้ที่จะเรียกใช้หลายเธรดในเวลาเดียวกัน แกนประมวลผลแต่ละตัวสามารถดำเนินการหนึ่งเธรดในเวลาใดก็ได้ ซึ่งหมายความว่าแอ็พพลิเคชัน Java ใดๆ เป็นแบบมัลติเธรดที่มีความสำคัญ และภายในโปรแกรมของคุณ หนึ่งเธรดต่อแกนประมวลผลสามารถทำงานพร้อมกันได้

แกนประมวลผลประกอบด้วยชุดของการลงทะเบียนที่อยู่ในหน่วยความจำ (ภายในแกน) มันดำเนินการกับข้อมูลการลงทะเบียนได้เร็วกว่าข้อมูลที่อยู่ในหน่วยความจำหลัก (RAM) ของคอมพิวเตอร์ นี่เป็นเพราะโปรเซสเซอร์สามารถเข้าถึงการลงทะเบียนเหล่านี้ได้เร็วกว่ามาก

ซีพียูแต่ละตัวสามารถมีชั้นแคชของตัวเองได้ โปรเซสเซอร์ที่ทันสมัยส่วนใหญ่มี โปรเซสเซอร์สามารถเข้าถึงแคชได้เร็วกว่าหน่วยความจำหลัก แต่ไม่เร็วเท่ากับการลงทะเบียนภายใน ค่าของความเร็วในการเข้าถึงแคชมีค่าประมาณระหว่างความเร็วการเข้าถึงของหน่วยความจำหลักและรีจิสเตอร์ภายใน

ยิ่งกว่านั้นโปรเซสเซอร์ยังมีที่สำหรับแคชหลายระดับ แต่สิ่งนี้ไม่สำคัญนักที่จะต้องรู้เพื่อที่จะเข้าใจว่าโมเดลหน่วยความจำ Java โต้ตอบกับหน่วยความจำฮาร์ดแวร์อย่างไร สิ่งสำคัญคือต้องรู้ว่าโปรเซสเซอร์อาจมีแคชในระดับหนึ่ง

คอมพิวเตอร์ทุกเครื่องก็มี RAM (พื้นที่หน่วยความจำหลัก) ในลักษณะเดียวกัน คอร์ทั้งหมดสามารถเข้าถึงหน่วยความจำหลักได้ พื้นที่หน่วยความจำหลักมักจะใหญ่กว่าหน่วยความจำแคชของแกนประมวลผล

ในขณะที่โปรเซสเซอร์ต้องการเข้าถึงหน่วยความจำหลัก โปรเซสเซอร์จะอ่านส่วนหนึ่งของโปรเซสเซอร์ลงในหน่วยความจำแคช นอกจากนี้ยังสามารถอ่านข้อมูลบางอย่างจากแคชไปยังรีจิสเตอร์ภายในและดำเนินการกับข้อมูลเหล่านั้นได้ เมื่อ CPU จำเป็นต้องเขียนผลลัพธ์กลับไปยังหน่วยความจำหลัก มันจะล้างข้อมูลจากการลงทะเบียนภายในไปยังแคช และในบางจุดไปยังหน่วยความจำหลัก

ข้อมูลที่จัดเก็บไว้ในแคชจะถูกล้างกลับไปยังหน่วยความจำหลักตามปกติ เมื่อโปรเซสเซอร์ต้องการเก็บสิ่งอื่นไว้ในแคช แคชมีความสามารถในการล้างหน่วยความจำและเขียนข้อมูลในเวลาเดียวกัน โปรเซสเซอร์ไม่จำเป็นต้องอ่านหรือเขียนแคชทั้งหมดทุกครั้งระหว่างการอัพเดต โดยปกติแคชจะได้รับการอัปเดตในหน่วยความจำขนาดเล็กซึ่งเรียกว่า "แคชไลน์" "บรรทัดแคช" อย่างน้อยหนึ่งรายการอาจถูกอ่านในหน่วยความจำแคช และอาจมีการล้างบรรทัดแคชหนึ่งรายการขึ้นไปกลับไปยังหน่วยความจำหลัก

รวมโมเดลหน่วยความจำ Java และสถาปัตยกรรมฮาร์ดแวร์หน่วยความจำ

ดังที่กล่าวไปแล้ว โมเดลหน่วยความจำ Java และสถาปัตยกรรมฮาร์ดแวร์หน่วยความจำนั้นแตกต่างกัน สถาปัตยกรรมฮาร์ดแวร์ไม่ได้แยกความแตกต่างระหว่างเธรดสแต็กและฮีป บนฮาร์ดแวร์ เธรดสแต็กและ HEAP (ฮีป) จะอยู่ในหน่วยความจำหลัก

บางส่วนของสแต็กและเธรดฮีปอาจมีอยู่ในแคชและรีจิสเตอร์ภายในของ CPU ในบางครั้ง สิ่งนี้แสดงในแผนภาพ:

กองเธรดและ HEAP

เมื่อวัตถุและตัวแปรสามารถจัดเก็บในพื้นที่ต่างๆ ของหน่วยความจำของคอมพิวเตอร์ ปัญหาบางอย่างอาจเกิดขึ้นได้ นี่คือสองรายการหลัก:

  • การมองเห็นการเปลี่ยนแปลงที่เธรดทำกับตัวแปรที่ใช้ร่วมกัน
  • สภาพการแข่งขันเมื่ออ่าน ตรวจสอบ และเขียนตัวแปรที่ใช้ร่วมกัน

ประเด็นทั้งสองนี้จะอธิบายไว้ด้านล่าง

การมองเห็นของวัตถุที่ใช้ร่วมกัน

หากเธรดตั้งแต่สองเธรดขึ้นไปใช้ออบเจกต์ร่วมกันโดยไม่ได้ใช้การประกาศหรือซิงโครไนซ์ที่เหมาะสม การเปลี่ยนแปลงออบเจกต์ที่ใช้ร่วมกันที่ทำโดยเธรดหนึ่งอาจไม่สามารถมองเห็นได้ในเธรดอื่นๆ

ลองนึกภาพว่าวัตถุที่ใช้ร่วมกันถูกจัดเก็บไว้ในหน่วยความจำหลัก เธรดที่ทำงานบน CPU จะอ่านวัตถุที่ใช้ร่วมกันลงในแคชของ CPU เดียวกัน ที่นั่นเขาทำการเปลี่ยนแปลงกับวัตถุ เธรดที่ทำงานบน CPU อื่นจนกว่าแคชของ CPU จะถูกล้างไปยังหน่วยความจำหลัก ดังนั้น แต่ละเธรดจะได้รับสำเนาของตัวเองของวัตถุที่ใช้ร่วมกัน แต่ละสำเนาจะอยู่ในแคช CPU แยกต่างหาก

แผนภาพต่อไปนี้แสดงโครงร่างของสถานการณ์นี้ เธรดหนึ่งที่ทำงานบน CPU ด้านซ้ายจะคัดลอกวัตถุที่ใช้ร่วมกันลงในแคชและเปลี่ยนค่าของการนับเป็น 2 การเปลี่ยนแปลงนี้มองไม่เห็นสำหรับเธรดอื่นๆ ที่ทำงานบน CPU ด้านขวา เนื่องจากการอัปเดตที่จะนับยังไม่ถูกล้างกลับไปยังหน่วยความจำหลัก

เพื่อแก้ปัญหานี้ คุณสามารถใช้คำหลักที่เปลี่ยนแปลงได้เมื่อประกาศตัวแปร สามารถมั่นใจได้ว่าตัวแปรที่กำหนดจะถูกอ่านโดยตรงจากหน่วยความจำหลัก และจะถูกเขียนกลับไปยังหน่วยความจำหลักเสมอเมื่อมีการอัพเดท

สภาพการแข่งขัน

ถ้าเธรดตั้งแต่สองเธรดขึ้นไปใช้ออบเจกต์เดียวกันร่วมกัน และเธรดอัพเดตตัวแปรมากกว่าหนึ่งเธรดในอ็อบเจกต์ที่ใช้ร่วมกันนั้น อาจเกิดสภาวะการแย่งชิง

ลองนึกภาพว่าเธรด A อ่านตัวแปรนับของออบเจกต์ที่ใช้ร่วมกันลงในแคชของตัวประมวลผล ลองนึกภาพว่าเธรด B ทำสิ่งเดียวกัน แต่ในแคชของโปรเซสเซอร์อื่น ตอนนี้เธรด A เพิ่ม 1 ให้กับค่าของการนับ และเธรด B ก็ทำเช่นเดียวกัน ตอนนี้ตัวแปรเพิ่มขึ้นสองครั้ง - แยกจากกันโดย +1 ในแคชของโปรเซสเซอร์แต่ละตัว

หากเพิ่มเหล่านี้ตามลำดับ ตัวแปรนับจะเพิ่มเป็นสองเท่าและเขียนกลับไปยังหน่วยความจำหลัก (ค่าเดิม + 2)

อย่างไรก็ตาม มีการเพิ่มทีละสองครั้งพร้อมกันโดยไม่มีการซิงโครไนซ์ที่เหมาะสม ไม่ว่าเธรดใด (A หรือ B) จะเขียนการนับเวอร์ชันที่อัปเดตลงในหน่วยความจำหลัก ค่าใหม่จะมากกว่าค่าเดิมเพียง 1 ค่าเท่านั้น แม้จะเพิ่มขึ้นสองครั้งก็ตาม

แผนผังนี้แสดงการเกิดปัญหาสภาพการแข่งขันที่อธิบายไว้ข้างต้น:

เพื่อแก้ปัญหานี้ คุณสามารถใช้ Java synchronized block บล็อกที่ซิงโครไนซ์ช่วยให้มั่นใจได้ว่ามีเพียงหนึ่งเธรดเท่านั้นที่สามารถเข้าสู่ส่วนสำคัญของโค้ดที่กำหนดในเวลาใดก็ได้

บล็อกที่ซิงโครไนซ์ยังรับประกันว่าตัวแปรทั้งหมดที่เข้าถึงภายในบล็อกที่ซิงโครไนซ์จะถูกอ่านจากหน่วยความจำหลัก และเมื่อเธรดออกจากบล็อกที่ซิงโครไนซ์ ตัวแปรที่อัปเดตทั้งหมดจะถูกล้างกลับไปยังหน่วยความจำหลัก โดยไม่คำนึงว่าตัวแปรนั้นถูกประกาศเปลี่ยนแปลงหรือไม่ก็ตาม

ความคิดเห็น
  • เป็นที่นิยม
  • ใหม่
  • เก่า
คุณต้องลงชื่อเข้าใช้เพื่อแสดงความคิดเห็น
หน้านี้ยังไม่มีความคิดเห็นใด ๆ