แนวทางปฏิบัติที่ดีที่สุดในการบังคับเก็บขยะใน C #


118

จากประสบการณ์ของฉันดูเหมือนว่าคนส่วนใหญ่จะบอกคุณว่าไม่ฉลาดที่จะบังคับเก็บขยะ แต่ในบางกรณีที่คุณกำลังทำงานกับวัตถุขนาดใหญ่ที่ไม่ได้รับการรวบรวมในรุ่น 0 เสมอไป แต่ที่หน่วยความจำเป็นปัญหาคือ ตกลงที่จะบังคับรวบรวม? มีแนวทางปฏิบัติที่ดีที่สุดสำหรับการทำเช่นนั้นหรือไม่?

คำตอบ:


112

แนวทางปฏิบัติที่ดีที่สุดคืออย่าฝืนเก็บขยะ

ตาม MSDN:

"เป็นไปได้ที่จะบังคับให้รวบรวมขยะโดยการโทรหา Collect แต่โดยส่วนใหญ่แล้วควรหลีกเลี่ยงเพราะอาจสร้างปัญหาด้านประสิทธิภาพ"

อย่างไรก็ตามหากคุณสามารถทดสอบโค้ดของคุณได้อย่างน่าเชื่อถือเพื่อยืนยันว่าการเรียก Collect () จะไม่ส่งผลเสียให้ดำเนินการต่อ ...

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

ลิงค์นี้มีคำแนะนำที่เป็นประโยชน์เกี่ยวกับการเพิ่มหน่วยความจำ / การเก็บขยะ ฯลฯ :

http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx


3
นอกจากนี้คุณยังสามารถตั้งค่าที่แตกต่างกันของ LatencyMode msdn.microsoft.com/en-us/library/bb384202.aspx
เดวิด d e C Freitas

4
หากวัตถุของคุณชี้ไปที่หน่วยความจำที่ไม่มีการจัดการคุณสามารถแจ้งให้ตัวรวบรวมขยะทราบผ่าน GC.AddMemoryPressure Api ( msdn.microsoft.com/en-us/library/… ) สิ่งนี้ทำให้ผู้รวบรวมขยะมีข้อมูลเพิ่มเติมเกี่ยวกับระบบของคุณโดยไม่รบกวนอัลกอริทึมการรวบรวม
Govert

+1: * ดูการใช้ "คำสั่งใช้" และอินเทอร์เฟซที่ระบุได้ * ฉันไม่คิดแม้แต่จะบังคับยกเว้นเป็นทางเลือกสุดท้าย - คำแนะนำที่ดี (อ่านว่า 'ข้อจำกัดความรับผิดชอบ') อย่างไรก็ตามฉันบังคับให้รวบรวมในการทดสอบหน่วยเพื่อจำลองการสูญเสียการอ้างอิงที่ใช้งานอยู่ในการดำเนินการส่วนหลัง - ในที่สุดก็โยนไฟล์TargetOfInvocationNullException.
IAbstract

33

ดูวิธีนี้ - การทิ้งขยะในครัวมีประสิทธิภาพมากกว่าเมื่อขยะอยู่ที่ 10% หรือปล่อยให้เต็มก่อนนำออก?

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

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

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


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

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

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

32

แนวทางปฏิบัติที่ดีที่สุดคืออย่าบังคับให้มีการเก็บขยะในกรณีส่วนใหญ่ (ทุกระบบที่ฉันเคยทำงานนั้นมีการบังคับเก็บขยะมีปัญหาที่ขีดเส้นใต้ซึ่งหากแก้ไขได้จะทำให้ไม่จำเป็นต้องบังคับเก็บขยะและเร่งระบบให้เร็วขึ้น)

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

อย่างไรก็ตามในการประมวลผลแบทช์บางประเภทคุณรู้จัก GC มากขึ้น เช่นพิจารณาแอปพลิเคชันที่

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

คุณอาจจะสามารถที่จะทำให้เป็นกรณี (หลังจากระมัดระวัง) การทดสอบว่าคุณควรจะบังคับให้มีการเก็บขยะเต็มรูปแบบหลังจากที่คุณมีการประมวลผลแต่ละไฟล์

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

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

ฉันอยากจะมี API การรวบรวมขยะเมื่อฉันสามารถให้คำแนะนำเกี่ยวกับสิ่งประเภทนี้ได้โดยไม่ต้องบังคับ GC ด้วยตนเอง

ดู " เคล็ดลับการแสดงของ Rico Mariani "


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

1
@AlanK ฉันชอบสิ่งนี้เหมือนตอนที่เด็ก ๆ กลับบ้านในวันนั้นมันเป็นช่วงเวลาที่ดีมากสำหรับผู้ช่วยครูในการจัดระเบียบที่ดีโดยที่เด็ก ๆ ไม่ต้องมาขวางทาง (ระบบล่าสุดที่ฉันทำงานอยู่เพิ่งเริ่มกระบวนการบริการใหม่ในช่วงเวลาดังกล่าวเนื่องจากการบังคับใช้ GC)
Ian Ringrose

21

ฉันคิดว่าตัวอย่างที่Rico Mariani ให้ไว้นั้นดี: อาจเหมาะสมที่จะเรียกใช้ GC หากมีการเปลี่ยนแปลงที่สำคัญในสถานะของแอปพลิเคชัน ตัวอย่างเช่นในโปรแกรมแก้ไขเอกสารอาจเป็นเรื่องปกติที่จะทริกเกอร์ GC เมื่อปิดเอกสาร


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

17

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

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

ปัญหาในการเขียนโปรแกรมมีหลากหลายและต้องการแนวทางที่ยืดหยุ่น ฉันเคยเห็นกรณีที่เหมาะสมที่จะบล็อก GC ในภาษาที่รวบรวมขยะและสถานที่ที่เหมาะสมที่จะเรียกใช้แทนที่จะรอให้เกิดขึ้นตามธรรมชาติ 95% ของเวลาทั้งสองอย่างนี้จะเป็นสัญญาณบ่งบอกถึงการไม่เข้าใกล้ปัญหาอย่างถูกต้อง แต่ 1 ครั้งใน 20 อาจมีกรณีที่ถูกต้องที่ต้องทำ


12

ฉันได้เรียนรู้ที่จะไม่พยายามชิงไหวชิงพริบในการเก็บขยะ ด้วยเหตุนี้ฉันจึงยึดติดกับการใช้usingคำสำคัญเมื่อจัดการกับทรัพยากรที่ไม่มีการจัดการเช่นไฟล์ I / O หรือการเชื่อมต่อฐานข้อมูล


27
คอมไพเลอร์? คอมไพเลอร์เกี่ยวข้องกับ GC อย่างไร :)
KristoferA

1
ไม่มีอะไรเลยเพียงรวบรวมและปรับโค้ดให้เหมาะสมเท่านั้น และสิ่งนี้ไม่มีส่วนเกี่ยวข้องกับ CLR ... หรือ. NET ด้วยซ้ำ
คอน

1
วัตถุที่มีหน่วยความจำจำนวนมากเช่นภาพขนาดใหญ่อาจไม่ได้รับการรวบรวมขยะเว้นแต่คุณจะเก็บขยะอย่างชัดเจน ฉันคิดว่านี่ (วัตถุขนาดใหญ่) เป็นปัญหาของ OP ไม่มากก็น้อย
code4life

1
การห่อโดยใช้จะช่วยให้มั่นใจได้ว่ามีการกำหนดเวลาสำหรับ GC เมื่อไม่ได้ใช้ขอบเขต หากคอมพิวเตอร์ไม่ระเบิดหน่วยความจำส่วนใหญ่มักจะถูกล้าง
คอน

9

ไม่แน่ใจว่าเป็นแนวทางปฏิบัติที่ดีที่สุดหรือไม่ แต่เมื่อทำงานกับรูปภาพจำนวนมากแบบวนซ้ำ (เช่นการสร้างและกำจัดออบเจ็กต์กราฟิก / รูปภาพ / บิตแมปจำนวนมาก) ฉันปล่อยให้ GC.Collect เป็นประจำ

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


แน่ใจหรือว่าต้องการสิ่งนี้ GC จะรวบรวมหากต้องการหน่วยความจำแม้ว่ารหัสของคุณจะไม่ได้ใช้งานก็ตาม
Konrad Rudolph

ไม่แน่ใจว่าตอนนี้เป็นอย่างไรใน. net 3.5 SP1 แต่ก่อนหน้านี้ (1.1 และฉันเชื่อว่าฉันทดสอบกับ 2.0) มันสร้างความแตกต่างในการใช้หน่วยความจำ แน่นอนว่า GC จะเก็บรวบรวมเสมอเมื่อจำเป็น แต่คุณอาจยังต้องเสีย RAM 100 Megs เมื่อคุณต้องการเพียง 20 แต่ก็ต้องมีการทดสอบอีกสองสามครั้ง
Michael Stum

2
GC ถูกทริกเกอร์ในการจัดสรรหน่วยความจำเมื่อรุ่น 0 ถึงเกณฑ์ที่กำหนด (เช่น 1MB) ไม่ใช่เมื่อ "มีบางอย่างไม่ได้ใช้งาน" มิฉะนั้นคุณอาจจบลงด้วย OutOfMemoryException ในการวนซ้ำเพียงแค่จัดสรรและทิ้งวัตถุทันที
liggett78

5
RAM 100megs จะไม่สูญเปล่าหากไม่มีกระบวนการอื่น ๆ ที่ต้องการ มันช่วยเพิ่มประสิทธิภาพที่ดีให้คุณ :-P
Orion Edwards

9

กรณีหนึ่งที่ฉันเพิ่งพบเมื่อเร็ว ๆ นี้ซึ่งจำเป็นต้องมีการโทรด้วยตนเองGC.Collect()คือเมื่อทำงานกับวัตถุ C ++ ขนาดใหญ่ที่ห่อหุ้มด้วยวัตถุ C ++ ที่มีการจัดการขนาดเล็กซึ่งจะเข้าถึงได้จาก C #

ตัวรวบรวมขยะไม่เคยถูกเรียกเนื่องจากจำนวนหน่วยความจำที่มีการจัดการที่ใช้นั้นมีน้อยมาก แต่จำนวนหน่วยความจำที่ไม่มีการจัดการที่ใช้นั้นมีมาก การเรียกDispose()วัตถุด้วยตนเองจะทำให้ฉันต้องคอยติดตามว่าเมื่อไหร่ที่วัตถุนั้นไม่จำเป็นต้องใช้ตัวเองอีกต่อไปในขณะที่การโทรGC.Collect()จะทำความสะอาดวัตถุใด ๆ ที่ไม่ได้อ้างถึงอีกต่อไป .....


6
วิธีที่ดีกว่าในการแก้ปัญหานี้คือการเรียกGC.AddMemoryPressure (ApproximateSizeOfUnmanagedResource)ใช้ตัวสร้างและต่อมาGC.RemoveMemoryPressure(addedSize)ในโปรแกรมสุดท้าย วิธีนี้เครื่องเก็บขยะจะทำงานโดยอัตโนมัติโดยคำนึงถึงขนาดของโครงสร้างที่ไม่มีการจัดการที่สามารถรวบรวมได้ stackoverflow.com/questions/1149181/…
HugoRune

และวิธีที่ดีกว่าในการแก้ปัญหาคือเรียก Dispose () ซึ่งคุณควรจะทำต่อไป
fabspro

2
วิธีที่ดีที่สุดคือใช้โครงสร้างการใช้ ลอง / สุดท้ายทิ้งเป็นเรื่องยุ่งยาก
TamusJRoyce

7

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

  1. คุณมีบางอย่างในรหัสที่ประกาศรายการในขอบเขตที่ใหญ่กว่าที่จำเป็นหรือไม่
  2. การใช้งานหน่วยความจำสูงเกินไปหรือไม่
  3. เปรียบเทียบประสิทธิภาพก่อนและหลังใช้ GC.Collect () เพื่อดูว่าช่วยได้จริงหรือไม่

5

สมมติว่าโปรแกรมของคุณไม่มีการรั่วไหลของหน่วยความจำวัตถุจะสะสมและไม่สามารถเป็น GC-ed ใน Gen 0 ได้เนื่องจาก: 1) มีการอ้างอิงเป็นเวลานานดังนั้นให้เข้าสู่ Gen1 & Gen2; 2) เป็นวัตถุขนาดใหญ่ (> 80K) ดังนั้นให้เข้าสู่ LOH (Large Object Heap) และ LOH ไม่ได้ทำการกระชับเช่นเดียวกับ Gen0, Gen1 & Gen2

ตรวจสอบตัวนับประสิทธิภาพของ ".NET Memory" คุณจะเห็นว่าปัญหา 1) ไม่ใช่ปัญหาจริงๆ โดยทั่วไปทุกๆ 10 Gen0 GC จะทริกเกอร์ 1 Gen1 GC และทุกๆ 10 Gen1 GC จะทริกเกอร์ 1 Gen2 GC ในทางทฤษฎี GC1 & GC2 ไม่สามารถเป็น GC-ed ได้หากไม่มีแรงกดดันต่อ GC0 (หากการใช้หน่วยความจำของโปรแกรมเป็นแบบใช้สายจริงๆ) มันไม่เคยเกิดขึ้นกับฉัน

สำหรับปัญหา 2) คุณสามารถตรวจสอบตัวนับประสิทธิภาพ ".NET Memory" เพื่อตรวจสอบว่า LOH เริ่มป่องหรือไม่ ถ้ามันเป็นปัญหาจริงๆปัญหาของคุณบางทีคุณสามารถสร้างขนาดใหญ่วัตถุสระว่ายน้ำเป็นบล็อกนี้แสดงให้เห็นhttp://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx


4

วัตถุขนาดใหญ่ได้รับการจัดสรรบน LOH (ฮีปวัตถุขนาดใหญ่) ไม่ใช่ใน gen 0 หากคุณบอกว่าวัตถุเหล่านั้นไม่ได้รับการรวบรวมขยะด้วย gen 0 คุณก็พูดถูก ฉันเชื่อว่าจะมีการรวบรวมเฉพาะเมื่อวงจร GC เต็ม (รุ่น 0, 1 และ 2) เกิดขึ้น

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

ยากที่จะบอกว่าจะรวบรวมหรือไม่และในสถานการณ์ใด ฉันเคยทำ GC.Collect () หลังจากทิ้งหน้าต่างโต้ตอบ / แบบฟอร์มที่มีการควบคุมมากมายเป็นต้น (เนื่องจากเมื่อถึงเวลาที่แบบฟอร์มและการควบคุมจะจบลงใน gen 2 เนื่องจากการสร้างอินสแตนซ์ของวัตถุทางธุรกิจจำนวนมาก / การโหลดข้อมูลจำนวนมาก - ไม่ใช่ วัตถุขนาดใหญ่อย่างเห็นได้ชัด) แต่จริงๆแล้วไม่ได้สังเกตเห็นผลบวกหรือลบใด ๆ ในระยะยาวโดยการทำเช่นนั้น


4

ฉันอยากจะเพิ่มว่า: การเรียก GC.Collect () (+ WaitForPendingFinalizers ()) เป็นส่วนหนึ่งของเรื่องราว ดังที่ผู้อื่นกล่าวไว้อย่างถูกต้อง GC.COllect () เป็นคอลเล็กชันที่ไม่ได้กำหนดและให้อยู่ในดุลยพินิจของ GC เอง (CLR) แม้ว่าคุณจะเพิ่มการเรียกไปยัง WaitForPendingFinalizers แต่ก็อาจไม่สามารถกำหนดได้ รับโค้ดจากลิงก์ msdn นี้และรันโค้ดด้วยการวนซ้ำของออบเจ็กต์เป็น 1 หรือ 2 คุณจะพบว่าความหมายที่ไม่ใช่ปัจจัยกำหนด (กำหนดจุดพักในตัวทำลายวัตถุ) อย่างแม่นยำไม่ได้เรียกตัวทำลายล้างเมื่อมีวัตถุค้างอยู่เพียง 1 (หรือ 2) ชิ้นโดย Wait .. () [อ้างอิง reqd.]

หากโค้ดของคุณจัดการกับทรัพยากรที่ไม่มีการจัดการ (เช่นตัวจัดการไฟล์ภายนอก) คุณต้องใช้ตัวทำลาย (หรือตัวสุดท้าย)

นี่คือตัวอย่างที่น่าสนใจ:

หมายเหตุ : หากคุณได้ลองใช้ตัวอย่างข้างต้นจาก MSDN แล้วรหัสต่อไปนี้จะทำการล้างอากาศ

class Program
{    
    static void Main(string[] args)
        {
            SomePublisher publisher = new SomePublisher();

            for (int i = 0; i < 10; i++)
            {
                SomeSubscriber subscriber = new SomeSubscriber(publisher);
                subscriber = null;
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.WriteLine(SomeSubscriber.Count.ToString());


            Console.ReadLine();
        }
    }

    public class SomePublisher
    {
        public event EventHandler SomeEvent;
    }

    public class SomeSubscriber
    {
        public static int Count;

        public SomeSubscriber(SomePublisher publisher)
        {
            publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
        }

        ~SomeSubscriber()
        {
            SomeSubscriber.Count++;
        }

        private void publisher_SomeEvent(object sender, EventArgs e)
        {
            // TODO: something
            string stub = "";
        }
    }

ฉันขอแนะนำให้วิเคราะห์ก่อนว่าผลลัพธ์จะเป็นอย่างไรจากนั้นจึงเรียกใช้จากนั้นอ่านเหตุผลด้านล่าง:

{ตัวทำลายจะถูกเรียกโดยปริยายเมื่อโปรแกรมสิ้นสุดเท่านั้น } ในการทำความสะอาดออบเจ็กต์อย่างกำหนดได้เราต้องใช้ IDisposable และทำการเรียกอย่างชัดเจนไปที่ Dispose () นั่นคือสาระสำคัญ! :)


2

อีกประการหนึ่งการเรียกใช้ GC Collect อย่างชัดเจนอาจไม่ช่วยปรับปรุงประสิทธิภาพโปรแกรมของคุณ ค่อนข้างเป็นไปได้ที่จะทำให้แย่ลง

.NET GC ได้รับการออกแบบและปรับแต่งมาเป็นอย่างดีเพื่อให้สามารถปรับเปลี่ยนได้ซึ่งหมายความว่าสามารถปรับเกณฑ์ GC0 / 1/2 ตาม "นิสัย" ของการใช้หน่วยความจำโปรแกรมของคุณ ดังนั้นมันจะถูกปรับให้เข้ากับโปรแกรมของคุณหลังจากทำงานไประยะหนึ่ง เมื่อคุณเรียกใช้ GC.Collect อย่างชัดเจนเกณฑ์จะถูกรีเซ็ต! และ. NET ต้องใช้เวลาปรับตัวให้เข้ากับ "นิสัย" ของโปรแกรมของคุณอีกครั้ง

ข้อเสนอแนะของฉันไว้วางใจ. NET GC เสมอ พบปัญหาหน่วยความจำตรวจสอบตัวนับประสิทธิภาพ ".NET Memory" และวินิจฉัยรหัสของฉันเอง


6
ฉันคิดว่าคุณจะรวมคำตอบนี้เข้ากับคำตอบก่อนหน้านี้ดีกว่า
Salamander2007

1

ไม่แน่ใจว่าเป็น Best Practice ...

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


0

อย่างไรก็ตามหากคุณสามารถทดสอบโค้ดของคุณได้อย่างน่าเชื่อถือเพื่อยืนยันว่าการเรียก Collect () จะไม่ส่งผลเสียให้ดำเนินการต่อ ...

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

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


"ถ้าอย่างนั้นคุณก็เข้าใจผิดหรือโปรแกรมของคุณถูกสร้างขึ้นผิดทั้งสองวิธีการแก้ปัญหาไม่ได้บังคับ GC ... " ค่าสัมบูรณ์มักไม่เป็นความจริง มีสถานการณ์พิเศษบางอย่างที่สมเหตุสมผล
ม้วน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.