ทำไมต้องมี Large Object Heap และทำไมเราถึงสนใจ?


107

ฉันได้อ่านเกี่ยวกับ Generations และ Large object heap แต่ฉันยังไม่เข้าใจว่าความสำคัญ (หรือประโยชน์) ของการมีกองวัตถุขนาดใหญ่คืออะไร?

อาจมีอะไรผิดพลาด (ในแง่ของประสิทธิภาพหรือหน่วยความจำ) หาก CLR เพิ่งใช้รุ่นที่ 2 (พิจารณาว่าเกณฑ์สำหรับ Gen0 และ Gen1 มีขนาดเล็กสำหรับจัดการวัตถุขนาดใหญ่) สำหรับการจัดเก็บวัตถุขนาดใหญ่


6
นี่ทำให้ฉันมีคำถามสองข้อสำหรับนักออกแบบ. NET: 1. เหตุใด LOH จึงไม่ถูกเรียกก่อนที่จะมีการโยน OutOfMemoryException 2. ทำไมไม่มีวัตถุ LOH ที่มีความสัมพันธ์ในการอยู่ร่วมกัน (ขนาดใหญ่ชอบจุดสิ้นสุดของกองและขนาดเล็กที่จุดเริ่มต้น)
Jacob Brewer

คำตอบ:


196

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

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

ดังนั้นพวกเขาจึงใช้เกณฑ์มาตรฐานจำนวนมากเพื่อกำหนดจุดคุ้มทุน และมาถึง 85,000 ไบต์เป็นจุดตัดที่การคัดลอกไม่ได้ปรับปรุงความสมบูรณ์แบบอีกต่อไป ด้วยข้อยกเว้นพิเศษสำหรับอาร์เรย์สองเท่าจะถือว่า 'ใหญ่' เมื่ออาร์เรย์มีองค์ประกอบมากกว่า 1,000 รายการ นั่นคือการเพิ่มประสิทธิภาพอีกอย่างหนึ่งสำหรับโค้ด 32 บิตตัวจัดสรรฮีปอ็อบเจ็กต์ขนาดใหญ่มีคุณสมบัติพิเศษที่จัดสรรหน่วยความจำที่แอดเดรสที่จัดแนวเป็น 8 ซึ่งแตกต่างจากตัวจัดสรรรุ่นปกติที่จัดสรรเฉพาะชิดกับ 4 การจัดตำแหน่งนั้นเป็นเรื่องใหญ่สำหรับสองเท่า การอ่านหรือเขียนคู่ผิดมีค่าใช้จ่ายสูงมาก ผิดปกติข้อมูล Microsoft ที่กระจัดกระจายไม่เคยพูดถึงอาร์เรย์ที่ยาวไม่แน่ใจว่าเกิดอะไรขึ้น

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

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

เรื่องสั้นยาว LOH ทำให้การทำงานของโค้ดมีประสิทธิภาพมากขึ้น เสียค่าใช้จ่ายในการใช้พื้นที่แอดเดรสหน่วยความจำเสมือนที่มีประสิทธิภาพน้อยกว่า


UPDATE, .NET 4.5.1 ในขณะนี้สนับสนุนการกระชับ LOH ที่GCSettings.LargeObjectHeapCompactionModeคุณสมบัติ โปรดระวังผลที่ตามมา


3
@Hans Passant คุณช่วยชี้แจงเกี่ยวกับระบบ x64 ได้ไหมคุณหมายถึงปัญหานี้หายไปโดยสิ้นเชิง?
Johnny_D

รายละเอียดการใช้งานบางอย่างของ LOH นั้นสมเหตุสมผล แต่ก็ทำให้ฉันไขปริศนาได้ ตัวอย่างเช่นฉันเข้าใจได้ว่าหากมีการสร้างและละทิ้งวัตถุขนาดใหญ่จำนวนมากโดยทั่วไปแล้วอาจเป็นที่พึงปรารถนาที่จะลบออกเป็นจำนวนมากในคอลเลกชัน Gen2 มากกว่าทีละชิ้นในคอลเลกชัน Gen0 แต่ถ้ามีการสร้างและละทิ้งเช่นอาร์เรย์ 22,000 สตริงซึ่ง ไม่มีการอ้างอิงภายนอกข้อได้เปรียบอะไรที่มีอยู่ในการมีคอลเลกชัน Gen0 และ Gen1 แท็กสตริงทั้งหมด 22,000 รายการเป็น "สด" โดยไม่คำนึงว่ามีการอ้างอิงใด ๆ กับอาร์เรย์หรือไม่
supercat

6
แน่นอนว่าปัญหาการแยกส่วนนั้นเหมือนกันบน x64 จะใช้เวลาเพียงไม่กี่วันในการเรียกใช้กระบวนการเซิร์ฟเวอร์ของคุณก่อนที่จะเริ่มใช้งาน
Lothar

1
อืมไม่อย่าประมาท 3 คำสั่งขนาด การเก็บขยะกองขยะ 4 เทราไบต์ใช้เวลานานเพียงใดเป็นสิ่งที่คุณไม่สามารถหลีกเลี่ยงการค้นพบได้นานก่อนที่มันจะเข้าใกล้ขนาดนั้น
Hans Passant

2
@HansPassant ได้โปรดอธิบายเพิ่มเติมเกี่ยวกับข้อความนี้: "ใช้เวลานานแค่ไหนในการรวบรวมกองขยะขนาด 4 เทราไบต์เป็นสิ่งที่คุณไม่สามารถหลีกเลี่ยงการค้นพบได้นานก่อนที่มันจะเข้าใกล้ขนาดนั้น"
ค่อนข้าง

9

หากขนาดของวัตถุมากกว่าค่าที่ตรึงไว้ (85000 ไบต์ใน. NET 1) CLR จะวางไว้ใน Large Object Heap สิ่งนี้จะเพิ่มประสิทธิภาพ:

  1. การจัดสรรวัตถุ (วัตถุขนาดเล็กไม่ผสมกับวัตถุขนาดใหญ่)
  2. การเก็บขยะ (LOH รวบรวมเฉพาะใน GC เต็มรูปแบบ)
  3. การจัดเรียงข้อมูลหน่วยความจำ (LOH ไม่เคยกระชับ)

9

ความแตกต่างที่สำคัญของ Small Object Heap (SOH) และ Large Object Heap (LOH) คือหน่วยความจำใน SOH จะถูกบีบอัดเมื่อรวบรวมในขณะที่ LOH ไม่ใช่ตามที่บทความนี้อธิบาย การบีบอัดวัตถุขนาดใหญ่มีค่าใช้จ่ายมาก เช่นเดียวกับตัวอย่างในบทความเช่นการย้ายไบต์ในหน่วยความจำต้องใช้ 2 รอบจากนั้นการบีบอัดวัตถุ 8MB ในคอมพิวเตอร์ 2GHz ต้องใช้ 8ms ซึ่งเป็นต้นทุนที่มาก การพิจารณาวัตถุขนาดใหญ่ (ในกรณีส่วนใหญ่อาร์เรย์) เป็นเรื่องปกติในทางปฏิบัติฉันคิดว่านั่นเป็นเหตุผลว่าทำไม Microsoft จึงตรึงวัตถุขนาดใหญ่ในหน่วยความจำและเสนอ LOH

BTW ตามโพสต์นี้ LOH มักจะไม่สร้างปัญหาการแยกส่วนหน่วยความจำ


1
การโหลดข้อมูลจำนวนมากลงในอ็อบเจ็กต์ที่มีการจัดการมักจะลดต้นทุน 8 มิลลิวินาทีในการบีบอัด LOH ในทางปฏิบัติในแอปพลิเคชันข้อมูลขนาดใหญ่ส่วนใหญ่ค่าใช้จ่าย LOH นั้นไม่สำคัญถัดจากประสิทธิภาพแอปพลิเคชันที่เหลือ
ชีฟ

3

หลักการสำคัญคือมันไม่น่าเป็นไปได้ (และการออกแบบที่ค่อนข้างแย่) ที่กระบวนการจะสร้างอ็อบเจ็กต์ขนาดใหญ่ที่มีอายุสั้นจำนวนมากดังนั้น CLR จึงจัดสรรอ็อบเจ็กต์ขนาดใหญ่ไปยังฮีปแยกต่างหากซึ่งจะรัน GC ตามกำหนดเวลาที่แตกต่างกันไปยังฮีพปกติ http://msdn.microsoft.com/en-us/magazine/cc534993.aspx


การวางวัตถุขนาดใหญ่ไว้ด้วยเช่นกันกล่าวว่ารุ่นที่ 2 อาจทำให้ประสิทธิภาพการทำงานแย่ลงเนื่องจากต้องใช้เวลานานในการกระชับหน่วยความจำโดยเฉพาะอย่างยิ่งหากมีการปลดปล่อยวัตถุจำนวนเล็กน้อยและต้องคัดลอกวัตถุขนาดใหญ่ไปยังตำแหน่งใหม่ LOH ปัจจุบันไม่ได้รับการบีบอัดเนื่องจากเหตุผลด้านประสิทธิภาพ
Christopher Currens

ฉันคิดว่ามันเป็นเพียงการออกแบบที่ไม่ดีเพราะ GC จัดการได้ไม่ดี
CodesInChaos

@CodeInChaos เห็นได้ชัดว่ามีการปรับปรุง
Christian.K

1
@CodeInChaos: แม้ว่าระบบจะรอจนกว่าคอลเลกชัน gen2 อาจสมเหตุสมผลก่อนที่จะพยายามเรียกคืนหน่วยความจำจากวัตถุ LOH ที่มีอายุสั้น แต่ฉันไม่เห็นข้อได้เปรียบด้านประสิทธิภาพใด ๆ ในการประกาศวัตถุ LOH (และวัตถุใด ๆ ที่พวกเขาถืออยู่ การอ้างอิง) ใช้งานได้อย่างไม่มีเงื่อนไขระหว่างคอลเล็กชัน gen0 และ gen1 มีการเพิ่มประสิทธิภาพบางอย่างที่ทำให้เกิดขึ้นได้จากสมมติฐานดังกล่าวหรือไม่?
supercat

@supercat ฉันดูลิงค์ที่กล่าวถึงโดย Myles McDonnell ความเข้าใจของฉันคือ 1. คอลเลกชัน LOH เกิดขึ้นใน Gen 2 GC 2. คอลเลกชัน LOH ไม่รวมการบดอัด (ตามเวลาที่เขียนบทความ) แต่จะทำเครื่องหมายวัตถุที่ตายแล้วว่าใช้ซ้ำได้และรูเหล่านี้จะรองรับการจัดสรร LOH ในอนาคตหากมีขนาดใหญ่พอ เนื่องจากจุดที่ 1 เมื่อพิจารณาว่า gen 2 GC จะช้าหากมีวัตถุจำนวนมากใน gen 2 ฉันคิดว่าควรหลีกเลี่ยงการใช้ LOH ให้มากที่สุดในกรณีนี้
robbie fan

0

ฉันไม่ใช่ผู้เชี่ยวชาญเกี่ยวกับ CLR แต่ฉันคิดว่าการมีฮีปเฉพาะสำหรับวัตถุขนาดใหญ่สามารถป้องกันการกวาด GC ที่ไม่จำเป็นของฮีปเจเนอเรชันที่มีอยู่ได้ การจัดสรรวัตถุขนาดใหญ่จำเป็นต้องมีหน่วยความจำว่างที่ต่อเนื่องกันจำนวนมาก เพื่อให้ได้สิ่งนั้นมาจาก "หลุม" ที่กระจัดกระจายในฮีปรุ่นคุณจำเป็นต้องมีการคอมไพล์บ่อยๆ (ซึ่งทำได้เฉพาะกับวงจร GC เท่านั้น)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.