คุณไม่สามารถสร้างงบแบบครอบคลุมเกี่ยวกับวิธีที่เหมาะสมในการใช้การใช้งานGC ทั้งหมด พวกเขาแตกต่างกันอย่างดุเดือด ดังนั้นฉันจะพูดกับ. NET ที่คุณอ้างถึงในตอนแรก
คุณต้องรู้จักพฤติกรรมของ GC อย่างใกล้ชิดเพื่อทำสิ่งนี้ด้วยเหตุผลหรือเหตุผลใด ๆ
คำแนะนำเดียวในคอลเลกชันที่ฉันสามารถให้คือ: ไม่เคยทำมัน
หากคุณรู้รายละเอียดที่ซับซ้อนของ GC อย่างแท้จริงคุณจะไม่ต้องการคำแนะนำของฉันดังนั้นมันจะไม่สำคัญ หากคุณยังไม่ทราบด้วยความมั่นใจ 100% จะช่วยได้และต้องดูออนไลน์และค้นหาคำตอบเช่นนี้: คุณไม่ควรโทร GC.Collectหรืออีกวิธีหนึ่ง: คุณควรเรียนรู้รายละเอียดว่า GC ทำงานอย่างไร ทั้งภายในและภายนอกและเพียงแล้วคุณจะรู้คำตอบ
มีที่เดียวที่ปลอดภัยที่จะต้องใช้ GC.Collect :
GC.Collect เป็น API ที่คุณสามารถใช้สำหรับกำหนดเวลาการทำโปรไฟล์ คุณสามารถสร้างโปรไฟล์อัลกอริทึมหนึ่งรวบรวมและทำอัลกอริทึมอื่นในทันทีหลังจากทราบว่า GC ของอัลโกแรกไม่ได้เกิดขึ้นระหว่างที่สองของคุณบิดเบือนผลลัพธ์
การทำโปรไฟล์แบบนี้เป็นครั้งเดียวที่ฉันจะแนะนำให้รวบรวมทุกคนด้วยตนเอง
ตัวอย่างที่มีการจัดต่อไป
กรณีใช้งานที่เป็นไปได้อย่างหนึ่งคือถ้าคุณโหลดสิ่งของที่มีขนาดใหญ่จริงๆพวกมันจะลงเอยใน Large Object Heap ซึ่งจะตรงไปที่ Gen 2 แม้ว่า Gen 2 อีกครั้งจะเป็นวัตถุที่มีอายุการใช้งานยาวนานเพราะมันรวบรวมได้ไม่บ่อยนัก หากคุณรู้ว่าคุณกำลังโหลดวัตถุที่มีอายุสั้น ๆ เข้ามาใน Gen 2 ไม่ว่าด้วยเหตุผลใดคุณสามารถลบมันออกได้เร็วขึ้นเพื่อทำให้ Gen 2 ของคุณเล็กลงและมันก็จะเร็วขึ้น
นี่เป็นตัวอย่างที่ดีที่สุดที่ฉันจะได้รับและมันก็ไม่ดี - แรงกดดัน LOH ที่คุณสร้างที่นี่จะทำให้คอลเลกชันบ่อยขึ้นและคอลเลกชันก็บ่อยมากเหมือนที่มันเป็น - โอกาสที่มันจะเคลียร์ LOH เช่นเดียวกับ เร็วเหมือนที่คุณกำลังเป่ามันด้วยวัตถุชั่วคราว ฉันก็ไม่ไว้ใจตัวเองที่จะเข้าใจความถี่คอลเลกชันที่ดีกว่า GC ตัวเอง - ความคืบหน้าโดยคนไกลไกลอย่างชาญฉลาดกว่าฉัน
ถ้าอย่างนั้นเรามาพูดถึงซีแมนทิกส์และกลไกบางอย่างใน. NET GC ... หรือ ..
ทุกสิ่งที่ฉันคิดว่าฉันรู้เกี่ยวกับ. NET GC
ได้โปรดใครก็ตามที่พบข้อผิดพลาดที่นี่ - โปรดแก้ไขให้ถูกต้อง GC จำนวนมากเป็นที่รู้จักกันดีว่าเป็นเวทมนตร์สีดำและในขณะที่ฉันพยายามที่จะบอกรายละเอียดที่ไม่แน่ใจฉันอาจยังคงมีบางอย่างผิดปกติ
ด้านล่างมีรายละเอียดมากมายที่ฉันไม่แน่ใจอย่างชัดเจนโดยเฉพาะรวมถึงเนื้อหาที่ใหญ่กว่าที่ฉันไม่รู้ ใช้ข้อมูลนี้โดยยอมรับความเสี่ยงของคุณเอง
แนวคิด GC
. NET GC เกิดขึ้นในเวลาที่ไม่สอดคล้องกันซึ่งเป็นสาเหตุที่เรียกว่า "ไม่ได้กำหนด" ซึ่งหมายความว่าคุณไม่สามารถเชื่อถือได้ในเวลาที่ระบุ นอกจากนี้ยังเป็นตัวเก็บขยะทั่วไปซึ่งหมายความว่ามันแบ่งพาร์ติชั่นของคุณออกเป็นจำนวน GC ที่พวกมันผ่านมา
วัตถุในกอง Gen 0 มีชีวิตอยู่ผ่านคอลเลกชัน 0 รายการเหล่านี้ถูกสร้างขึ้นใหม่ดังนั้นเมื่อเร็ว ๆ นี้จึงไม่มีการรวบรวมเกิดขึ้นตั้งแต่การเริ่มต้น วัตถุในฮีป Gen 1 ของคุณมีชีวิตอยู่ผ่านคอลเล็กชั่นสะสมหนึ่งและไอเท็มในฮีป Gen 2 ของคุณก็มีชีวิตผ่านคอลเล็กชั่น 2 ใบ
ตอนนี้มันก็คุ้มค่าที่จะสังเกตว่ามันมีคุณสมบัติตามรุ่นและพาร์ติชั่นเหล่านี้ .NET GC รับรู้เฉพาะสามชั่วอายุคนเหล่านี้เท่านั้นเนื่องจากคอลเลกชันที่ส่งผ่าน heaps ทั้งสามนี้จะแตกต่างกันเล็กน้อย วัตถุบางอย่างอาจรอดจากการสะสมนับพันครั้ง GC เพิ่งทิ้งสิ่งเหล่านี้ไว้ที่อีกด้านหนึ่งของพาร์ติชั่นฮีป Gen 2, ไม่มีจุดใดที่จะแยกพาร์ติชั่นเหล่านั้นออกไปได้อีกเนื่องจากมันเป็น Gen 44 จริง ๆ ; การส่งผ่านคอลเลกชันจะเหมือนกับทุกอย่างในกอง Gen 2
มีจุดประสงค์เชิงความหมายสำหรับคนรุ่นนี้โดยเฉพาะรวมถึงกลไกที่นำมาใช้เพื่อเป็นเกียรติแก่สิ่งเหล่านี้และฉันจะไปหาคนเหล่านั้นในเวลาไม่นาน
มีอะไรในคอลเลกชัน
แนวคิดพื้นฐานของรหัสผ่านการรวบรวม GC คือการตรวจสอบแต่ละวัตถุในพื้นที่ฮีปเพื่อดูว่ายังมีการอ้างอิงสด (ราก GC) ต่อวัตถุเหล่านี้หรือไม่ หากพบรูต GC สำหรับวัตถุหมายความว่าขณะนี้ยังสามารถเข้าถึงและใช้รหัสนั้นได้ซึ่งหมายความว่าขณะนี้ยังสามารถเข้าถึงและใช้วัตถุนั้นได้ดังนั้นจึงไม่สามารถลบได้ อย่างไรก็ตามหากไม่พบราก GC สำหรับวัตถุหมายความว่ากระบวนการที่กำลังทำงานไม่ต้องการวัตถุอีกต่อไปดังนั้นจึงสามารถลบออกเพื่อเพิ่มหน่วยความจำสำหรับวัตถุใหม่ได้
หลังจากเสร็จสิ้นการทำความสะอาดวัตถุจำนวนมากและทิ้งไว้คนเดียวจะมีผลข้างเคียงที่โชคร้าย: ช่องว่างว่างระหว่างวัตถุที่มีชีวิตซึ่งวัตถุที่ถูกกำจัดออกไป การแตกแฟรกเมนต์ของหน่วยความจำนี้ถ้าทิ้งไว้ตามลำพังก็จะทำให้หน่วยความจำเสียดังนั้นโดยทั่วไปคอลเลกชันจะทำสิ่งที่เรียกว่า "การบดอัด" โดยที่พวกเขานำวัตถุที่มีชีวิตทั้งหมดที่เหลืออยู่แล้วบีบเข้าด้วยกัน 0
ตอนนี้ให้แนวคิดของหน่วยความจำ 3 ฮีปทั้งหมดที่แบ่งตามจำนวนคอลเล็กชันที่ผ่านมาเรามาพูดกันว่าทำไมพาร์ติชั่นเหล่านี้ถึงมีอยู่
การสะสม Gen 0
Gen 0 เป็นวัตถุใหม่ล่าสุดแน่นอนมีแนวโน้มที่จะมีขนาดเล็กมาก - เพื่อให้คุณสามารถเก็บมันบ่อยมาก ความถี่ทำให้แน่ใจว่าฮีปมีขนาดเล็กและคอลเล็กชั่นนั้นเร็วมากเพราะเก็บสะสมฮีปขนาดเล็กเช่นนั้น สิ่งนี้ขึ้นอยู่กับฮิวริสติกที่อ้างว่ามีมากขึ้นหรือน้อย: วัตถุชั่วคราวส่วนใหญ่ที่คุณสร้างเป็นชั่วคราวมากดังนั้นชั่วคราวจะไม่ถูกใช้หรืออ้างถึงอีกต่อไปทันทีหลังการใช้งานและสามารถเก็บได้
การสะสม Gen 1
Gen 1 ถูกวัตถุที่ไม่ได้ตกอยู่ในนี้มากหมวดหมู่ชั่วคราวของวัตถุอาจจะยังคงอาศัยอยู่ค่อนข้างสั้นเพราะ still- ส่วนใหญ่ของวัตถุที่สร้างขึ้นจะไม่ใช้เวลานาน ดังนั้น Gen 1 จึงเก็บสะสมค่อนข้างบ่อยอีกครั้งทำให้มันมีขนาดเล็กมากดังนั้นคอลเล็กชั่นจึงรวดเร็ว อย่างไรก็ตามสมมติฐานที่เป็นน้อยของวัตถุมันเป็นชั่วคราวกว่า Gen 0 จึงเก็บน้อยกว่า Gen 0
ฉันจะบอกว่าฉันไม่ทราบแน่ชัดว่ากลไกทางเทคนิคที่แตกต่างระหว่างการสะสมของ Gen 0 กับ Gen 1 นั้นมีอะไรนอกเหนือจากความถี่ที่พวกเขาเก็บรวบรวม
การสะสม Gen 2
Gen 2 ตอนนี้ต้องเป็นแม่ของกองทั้งหมดใช่มั้ย อืมใช่นั่นถูกหรือมากกว่า มันคือที่ที่วัตถุถาวรทั้งหมดของคุณมีชีวิต - วัตถุMain()
ในชีวิตของคุณเป็นต้นและทุกสิ่งที่Main()
อ้างอิงเพราะสิ่งเหล่านั้นจะถูกรูทจนกว่าคุณMain()
จะกลับมาในตอนท้ายของกระบวนการ
เนื่องจาก Gen 2 เป็นถังสำหรับทุกสิ่งที่คนรุ่นอื่นไม่สามารถรวบรวมได้มันเป็นวัตถุส่วนใหญ่ที่ถาวรหรือมีอายุน้อยที่สุด ดังนั้นการตระหนักถึงสิ่งที่น้อยมากใน Gen 2 จริง ๆ แล้วจะเป็นสิ่งที่สามารถรวบรวมได้มันไม่จำเป็นต้องรวบรวมบ่อยครั้ง สิ่งนี้ทำให้คอลเลกชันของมันช้าลงเช่นกันเนื่องจากมันทำงานบ่อยครั้งมาก ดังนั้นนี่คือพื้นฐานที่พวกเขาได้จัดการกับพฤติกรรมพิเศษทั้งหมดสำหรับสถานการณ์แปลก ๆ เพราะพวกเขามีเวลาดำเนินการ
กองวัตถุขนาดใหญ่
ตัวอย่างหนึ่งของพฤติกรรมพิเศษ Gen 2 ก็คือว่ามันยังไม่คอลเลกชันบนกองวัตถุขนาดใหญ่ จนถึงตอนนี้ฉันได้พูดถึงเรื่อง Small Object Heap อย่างสมบูรณ์แล้ว แต่. NET runtime จะจัดสรรสิ่งต่าง ๆ ในขนาดที่แน่นอนให้เป็นฮีปแยกต่างหากเนื่องจากสิ่งที่ฉันเรียกว่าการบีบอัดด้านบน การบดอัดต้องมีการเคลื่อนย้ายวัตถุรอบ ๆ เมื่อการรวบรวมเสร็จสิ้นใน Small Object Heap หากมีวัตถุ 10mb ที่ยังมีชีวิตอยู่ใน Gen 1 มันจะต้องใช้เวลานานกว่านั้นในการทำการบดอัดให้เสร็จหลังจากการสะสมดังนั้นจึงทำให้การสะสมของ Gen 1 ช้าลง ดังนั้นวัตถุ 10mb จะถูกจัดสรรให้กับ Large Object Heap และถูกรวบรวมระหว่าง Gen 2 ซึ่งทำงานนาน ๆ ครั้ง
สรุป
อีกตัวอย่างคือวัตถุที่มี finalizers คุณวาง finalizer บนวัตถุที่อ้างอิงทรัพยากรเกินขอบเขตของ. NETs GC (ทรัพยากรที่ไม่มีการจัดการ) finalizer เป็นวิธีเดียวที่ GC เรียกร้องให้มีการรวบรวมทรัพยากรที่ไม่มีการจัดการ - คุณใช้ finalizer ของคุณเพื่อทำการรวบรวม / กำจัด / ปล่อยทรัพยากรที่ไม่มีการจัดการด้วยตนเองเพื่อให้แน่ใจว่าจะไม่รั่วไหลจากกระบวนการของคุณ เมื่อ GC เข้าสู่การดำเนินการ finalizer วัตถุของคุณแล้วการดำเนินการของคุณจะล้างทรัพยากรที่ไม่มีการจัดการทำให้ GC สามารถลบวัตถุของคุณโดยไม่เสี่ยงต่อการรั่วไหลของทรัพยากร
กลไกที่ finalizers ทำคือการอ้างอิงโดยตรงในคิวการสรุป เมื่อรันไทม์จัดสรรวัตถุด้วย finalizer มันจะเพิ่มตัวชี้ไปยังวัตถุนั้นในคิวการสรุปและล็อควัตถุของคุณให้เข้าที่ (เรียกว่าการปักหมุด) ดังนั้นการบีบอัดจะไม่ย้ายซึ่งจะเป็นการทำลายการอ้างอิงคิวสุดท้าย เมื่อการรวบรวมผ่านเกิดขึ้นในที่สุดวัตถุของคุณจะพบว่าไม่มีราก GC อีกต่อไป แต่ต้องดำเนินการเสร็จสิ้นก่อนจึงจะสามารถรวบรวมได้ ดังนั้นเมื่อวัตถุนั้นตายแล้วคอลเลคชั่นจะย้ายมันคือการอ้างอิงจากคิวการสรุปและวางการอ้างอิงไปยังสิ่งที่เรียกว่าคิว "FReachable" จากนั้นคอลเลกชันยังคงดำเนินต่อไป อีกครั้ง "ไม่กำหนด" ในอนาคต เธรดแยกที่เรียกว่าเธรด Finalizer จะผ่านคิว FReachable โดยดำเนินการ finalizers สำหรับแต่ละออบเจ็กต์ที่อ้างอิง หลังจากเสร็จสิ้นคิว FReachable จะว่างเปล่าและมีการพลิกบิตบนส่วนหัวของแต่ละวัตถุที่ระบุว่าพวกเขาไม่ต้องการการสรุป (บิตนี้สามารถพลิกด้วยตนเองด้วยGC.SuppressFinalize
ซึ่งเป็นเรื่องธรรมดาในDispose()
วิธีการ) ฉันยังสงสัยว่ามันไม่ได้ตรึงวัตถุ แต่ไม่ได้พูดถึงฉัน คอลเล็กชันถัดไปที่มากับสิ่งที่กองวัตถุนี้อยู่จะถูกรวบรวมในที่สุด คอลเลกชัน Gen 0 ไม่สนใจวัตถุที่เปิดใช้งานบิตสุดท้ายนั้นจะทำการโปรโมตมันโดยอัตโนมัติโดยไม่ต้องตรวจสอบหาราก วัตถุที่ไม่ได้ผ่านการรูทซึ่งต้องการการสรุปใน Gen 1 จะถูกโยนลงในFReachable
คิว แต่คอลเลกชันจะไม่ทำสิ่งใดกับมันดังนั้นมันจึงอยู่ใน Gen 2 ด้วยวิธีนี้วัตถุทั้งหมดที่มี finalizer และไม่GC.SuppressFinalize
จะถูกรวบรวมใน Gen 2