ฟิลด์ดั้งเดิมถูกเก็บไว้ที่ไหน
สาขาดั้งเดิมจะถูกเก็บไว้เป็นส่วนหนึ่งของวัตถุที่ถูกสร้างที่ไหนสักแห่ง วิธีที่ง่ายที่สุดที่จะคิดว่านี่คือที่ไหน - คือกอง อย่างไรก็ตามนี่ไม่ใช่กรณีเสมอไป ตามที่อธิบายในทฤษฎีและการปฏิบัติของ Java: Urban performance legends, revisited :
JVM สามารถใช้เทคนิคที่เรียกว่าการวิเคราะห์แบบหลีกเลี่ยงซึ่งพวกเขาสามารถบอกได้ว่าวัตถุบางอย่างยังคง จำกัด อยู่ในเธรดเดียวตลอดอายุการใช้งานและอายุการใช้งานนั้นถูก จำกัด ด้วยอายุการใช้งานของกรอบสแต็กที่กำหนด วัตถุดังกล่าวสามารถจัดสรรได้อย่างปลอดภัยบนกองซ้อนแทนที่จะเป็นกอง ยิ่งไปกว่านั้นสำหรับวัตถุขนาดเล็ก JVM สามารถปรับการจัดสรรให้เหมาะสมที่สุดและเพียงแค่ยกฟิลด์ของวัตถุลงในทะเบียน
ดังนั้นนอกเหนือจากการพูดว่า "วัตถุถูกสร้างขึ้นและเขตข้อมูลอยู่ที่นั่นด้วย" เราไม่สามารถพูดได้ว่ามีบางสิ่งที่อยู่ในกองหรือกองซ้อน โปรดทราบว่าสำหรับวัตถุขนาดเล็กที่มีอายุสั้นอาจเป็นไปได้ว่า 'วัตถุ' จะไม่มีอยู่ในหน่วยความจำเช่นนั้นและอาจวางเขตข้อมูลไว้ในรีจิสเตอร์โดยตรง
กระดาษสรุปด้วย:
JVM นั้นเก่งในการค้นหาสิ่งต่าง ๆ ที่เราเคยคาดเดาว่านักพัฒนาซอฟต์แวร์จะได้รู้ โดยการให้ JVM เลือกระหว่างการจัดสรรสแต็กและการจัดสรรฮีปเป็นกรณี ๆ ไปเราสามารถได้รับประโยชน์ด้านประสิทธิภาพของการจัดสรรสแต็กโดยไม่ทำให้โปรแกรมเมอร์รู้สึกเจ็บปวดมากกว่าว่าจะจัดสรรบนสแต็กหรือบนฮีป
ดังนั้นหากคุณมีรหัสที่ดูเหมือนว่า:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
โดยที่...
ไม่อนุญาตให้qux
ออกจากขอบเขตนั้นqux
อาจถูกจัดสรรในสแต็กแทน นี่เป็นความจริงที่ชนะสำหรับ VM เพราะมันหมายความว่าไม่จำเป็นต้องมีการรวบรวมขยะ - มันจะหายไปเมื่อมันออกจากขอบเขต
เพิ่มเติมเกี่ยวกับการวิเคราะห์การหลบหนีที่ Wikipedia สำหรับผู้ที่ต้องการเจาะลึกลงไปในเอกสารEscape Analysis for Javaจาก IBM สำหรับผู้ที่มาจากโลก C # คุณอาจพบรายละเอียดการดำเนินการและความจริงเกี่ยวกับประเภทค่าโดย Eric Lippert อ่านดี (พวกมันมีประโยชน์สำหรับประเภท Java เช่นเดียวกับแนวคิดและแง่มุมต่าง ๆ ที่เหมือนกันหรือคล้ายกัน) . ทำไมหนังสือ. Net พูดถึงการจัดสรรหน่วยความจำ stack vs heap ยังไปในนี้
บนกองของกองและกอง
บนกอง
เหตุใดจึงมีกองหรือกองเลย? สำหรับสิ่งต่าง ๆ ที่ออกจากขอบเขต พิจารณารหัส:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
พารามิเตอร์เป็นส่วนหนึ่งของสแต็กด้วย ในสถานการณ์ที่คุณไม่มีฮีปคุณจะผ่านชุดเต็มของค่าในสแต็ก นี่เป็นสิ่งที่ดี"foo"
และสตริงเล็ก ๆ ... แต่จะเกิดอะไรขึ้นถ้ามีคนใส่ไฟล์ XML ขนาดใหญ่ไว้ในสตริงนั้น การโทรแต่ละครั้งจะคัดลอกสตริงขนาดใหญ่ทั้งหมดลงในสแต็ก - และนั่นจะค่อนข้างสิ้นเปลือง
แต่จะเป็นการดีกว่าถ้าวางวัตถุที่มีชีวิตนอกขอบเขตทันที (ส่งไปยังขอบเขตอื่นติดอยู่ในโครงสร้างที่คนอื่นกำลังดูแล ฯลฯ ) ไปยังพื้นที่อื่นที่เรียกว่าฮีป
บนสแต็ค
คุณไม่ต้องการสแต็ค หนึ่งอาจสมมุติเขียนภาษาที่ไม่ได้ใช้สแต็ค (ของความลึกโดยพลการ) พื้นฐานเก่าที่ฉันได้เรียนรู้ในวัยเยาว์ของฉันทำเช่นนั้นเราทำได้เพียง 8 ระดับการgosub
โทรเท่านั้นและตัวแปรทั้งหมดเป็นโลก - ไม่มีสแต็ค
ข้อดีของสแต็กคือเมื่อคุณมีตัวแปรที่มีอยู่ในขอบเขตเมื่อคุณออกจากขอบเขตนั้นเฟรมสแต็กนั้นจะผุดขึ้น มันลดความซับซ้อนของสิ่งที่มีอยู่และสิ่งที่ไม่มี โปรแกรมย้ายไปยังโพรซีเดอร์อื่นเฟรมสแต็กใหม่ โปรแกรมจะกลับไปที่โพรซีเดอร์และคุณกลับมาในโพรเซสที่เห็นขอบเขตปัจจุบันของคุณ โปรแกรมจะออกจากโพรซีเดอร์และรายการทั้งหมดในสแต็กจะถูกจัดสรรคืน
สิ่งนี้ทำให้ชีวิตง่ายขึ้นสำหรับผู้ที่เขียนรันไทม์สำหรับโค้ดเพื่อใช้สแต็กและฮีป พวกเขามีเพียงแนวคิดและวิธีการทำงานกับโค้ดที่อนุญาตให้ผู้เขียนโค้ดในภาษามีอิสระในการคิดอย่างชัดเจน
ลักษณะของสแต็กยังหมายความว่ามันไม่สามารถแยกส่วนได้ การแตกแฟรกเมนต์ของหน่วยความจำเป็นปัญหาจริงของฮีป คุณจัดสรรวัตถุจำนวนหนึ่งแล้วขยะจะรวบรวมวัตถุกึ่งกลางจากนั้นลองค้นหาพื้นที่สำหรับวัตถุขนาดใหญ่ถัดไปที่จะจัดสรร มันเป็นระเบียบ ความสามารถในการวางสิ่งต่าง ๆ ลงบนสแต็กแทนหมายความว่าคุณไม่ต้องจัดการกับสิ่งนั้น
เมื่อสิ่งที่เป็นขยะที่เก็บรวบรวม
เมื่อสิ่งที่เก็บขยะมันหายไป แต่มันเป็นเพียงขยะที่เก็บรวบรวมเพราะมันถูกลืมไปแล้ว - ไม่มีการอ้างอิงถึงวัตถุในโปรแกรมที่สามารถเข้าถึงได้จากสถานะปัจจุบันของโปรแกรม
ฉันจะชี้ให้เห็นว่านี่เป็นการรวบรวมขยะขนาดใหญ่มาก ๆ (แม้ใน Java - คุณสามารถปรับแต่งตัวรวบรวมขยะโดยใช้ค่าสถานะต่าง ๆ ( docs ) สิ่งเหล่านี้ทำงานแตกต่างกันและความแตกต่างของวิธีการที่แต่ละคนทำสิ่งต่าง ๆ ลึกเกินไปสำหรับคำตอบนี้คุณอาจต้องการอ่านJava Garbage Collection Basicsเพื่อให้ได้แนวคิดที่ดีขึ้นเกี่ยวกับวิธีการทำงานบางอย่าง
ที่กล่าวว่าหากมีการจัดสรรบางสิ่งบางอย่างบนสแต็กจะไม่ถูกรวบรวมขยะเป็นส่วนหนึ่งของSystem.gc()
- มันจะถูกจัดสรรคืนเมื่อสแต็กเฟรมปรากฏขึ้น หากมีบางสิ่งอยู่ในฮีปและถูกอ้างอิงจากบางสิ่งบนสแต็กมันจะไม่ถูกรวบรวมขยะในเวลานั้น
เหตุใดเรื่องนี้
ส่วนใหญ่เป็นประเพณีของมัน หนังสือเรียนที่เขียนและเรียบเรียงคอมไพเลอร์และเอกสารประกอบบิตต่าง ๆ ทำให้เป็นเรื่องใหญ่เกี่ยวกับ heap และ stack
อย่างไรก็ตามเครื่องเสมือนของวันนี้ (JVM และที่คล้ายกัน) มีความพยายามอย่างมากในการพยายามซ่อนสิ่งนี้ไว้จากโปรแกรมเมอร์ เว้นแต่คุณจะหมดหนึ่งและอื่น ๆ และจำเป็นต้องรู้ว่าทำไม (แทนที่จะเพิ่มพื้นที่เก็บข้อมูลอย่างเหมาะสม) มันไม่สำคัญมากเกินไป
วัตถุอยู่ที่ไหนสักแห่งและอยู่ในสถานที่ที่สามารถเข้าถึงได้อย่างถูกต้องและรวดเร็วตามระยะเวลาที่เหมาะสม หากอยู่ในกองหรือกอง - มันไม่สำคัญ