มีอะไรผิดปกติเกี่ยวกับการใช้ GC.Collect ()?


103

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

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

คำถาม: ในกรณีนี้ไม่ควรเรียก GC.Collect () ก่อนเข้าสู่ขั้นตอนที่สองหรือไม่?

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

หมายเหตุ: ดังที่คุณบางคนได้ชี้ให้เห็นว่า. NET อาจไม่ใช่แพลตฟอร์มที่ดีที่สุดสำหรับแอปพลิเคชันเช่นนี้ แต่อยู่นอกเหนือขอบเขตของคำถามนี้ มีวัตถุประสงค์เพื่อชี้แจงว่าการเรียก GC.Collect () สามารถปรับปรุงพฤติกรรม / ประสิทธิภาพโดยรวมของแอปพลิเคชันได้หรือไม่ เราทุกคนยอมรับว่าสถานการณ์ที่คุณจะทำสิ่งนั้นหายากมาก แต่แล้วอีกครั้ง GC พยายามที่จะคาดเดาและทำได้ดีเกือบตลอดเวลา แต่ก็ยังเกี่ยวกับการคาดเดา

ขอบคุณ.


24
"การเปลี่ยนแปลงเพียงเล็กน้อยในโฟลว์ของโปรแกรมอาจทำให้เกิดหายนะ ... อาจมีคนตาย" - คุณแน่ใจหรือไม่ว่า C # .NET สามารถกำหนดได้อย่างเพียงพอสำหรับวัตถุประสงค์ของคุณ?
Steve Jessop

4
ไม่ใช่ Windows หรือ. NET เป็นแพลตฟอร์มแบบเรียลไทม์ดังนั้นคุณจึงไม่สามารถรับประกันเมตริกประสิทธิภาพได้อย่างน้อยก็ไม่เพียงพอที่จะเสี่ยงต่อชีวิตมนุษย์ ฉันเห็นด้วยกับ onebyone ว่าคุณพูดเกินจริงหรือประมาท
Sergio Acosta

3
ฮ่า ๆ ที่ "หนึ่งในสิ่งเหล่านี้ที่โปรแกรมเมอร์ผู้มีหน้ามีตาไม่เคยใช้แม้แต่คนที่ไม่รู้ด้วยซ้ำว่ามีไว้เพื่ออะไร"! โปรแกรมเมอร์ที่ใช้สิ่งของโดยไม่รู้ว่าทำไมหนังสือของฉันถึงไม่ค่อยมีหน้ามีตา :)
The Dag

คำตอบ:


87

จากบล็อกของ Rico ...

กฎ # 1

อย่า.

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

กฎ # 2

ลองโทรไปที่ GC.Collect () หากมีเหตุการณ์ที่ไม่เกิดขึ้นซ้ำเกิดขึ้นและเหตุการณ์นี้มีแนวโน้มสูงที่จะทำให้วัตถุเก่าจำนวนมากตาย

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

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

กฎ # 1 ควรเอาชนะกฎ # 2 โดยไม่มีหลักฐานที่ชัดเจน

วัด, วัด, วัด.


9
ฉันจะบอกว่านี่เป็นเพียงสิ่งเก่า ไม่มีอะไรเลวร้ายหรืออันตรายจริงๆถ้าคุณรู้ว่าคุณกำลังทำอะไรและรู้ว่าจะต้องทำเมื่อใดและอย่างไรรวมถึงผลข้างเคียง สิ่งที่ไม่เคยมีมาก่อนเคยใช้ xxxx เพื่อปกป้องโลกจากโปรแกรมเมอร์ที่มีหมัด: D
Jorge Córdoba


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

58

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


3
นั่นเป็นเรื่องจริง แต่ฉันไม่รู้ว่าพวกเขาสามารถตั้งสมมติฐานที่ใช้กับการพัฒนาทั้งหมดได้หรือไม่
MasterMastic

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

2
@TheDag IMO แน่นอนฉันเป็น เมื่อฉันปล่อยหน่วยความจำและอะไรก็ตามฉันไม่ได้สนใจฮาร์ดแวร์เพราะนั่นเป็นงานของระบบปฏิบัติการที่ต้องจัดการกับสิ่งนั้น ฉันยังไม่สนใจเกี่ยวกับระบบปฏิบัติการเพราะฉันมีอินเทอร์เฟซทั่วไปสำหรับทุกสิ่งที่ฉันกำลังเขียนโปรแกรม (เช่นฉันไม่สนใจว่าเป็น Windows, Mac หรือ Linux: เมื่อฉันจัดสรร / เพิ่มหน่วยความจำใน C / C ++ ใหม่ / ลบ malloc / dealloc) ฉันอาจผิดพลาดได้เสมอดังนั้นอย่าลังเลที่จะแก้ไขฉัน
MasterMastic

@MasterMastic mallocมีเพียงอินเทอร์เฟซที่เรียบง่ายและการใช้งานอาจแตกต่างกันไปมากพอสมควร ทุกอย่างขึ้นอยู่กับประเภทของปัญหาที่คุณกำลังพยายามแก้ไข ถ้าmalloc"ดีพอ" คุณไม่จำเป็นต้องรวมบัฟเฟอร์ใช่ไหม การพัฒนา C / C ++ เต็มไปด้วยตัวอย่างที่คุณพยายามเดาระบบปฏิบัติการ / รันไทม์ / ไลบรารีเป็นครั้งที่สองเพราะคุณรู้ดีกว่า (และบางครั้งคุณก็ทำได้จริงๆ) แอปพลิเคชันที่มีความสำคัญต่อประสิทธิภาพจำนวนมากหลีกเลี่ยงการใช้ตัวจัดสรรระบบ / รันไทม์โดยสิ้นเชิง เกมที่ใช้ในการจัดสรรหน่วยความจำทั้งหมดไว้ล่วงหน้าเมื่อเริ่มต้น (อาร์เรย์ขนาดคงที่เป็นต้น)
Luaan

24

แล้วเวลาที่คุณใช้วัตถุ COM เช่น MS Word หรือ MS Excel จาก. NET ล่ะ? โดยไม่ต้องเรียกGC.Collectหลังจากปล่อยวัตถุ COM เราพบว่ายังคงมีอินสแตนซ์ของแอปพลิเคชัน Word หรือ Excel อยู่

ในความเป็นจริงรหัสที่เราใช้คือ:

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

นั่นจะเป็นการใช้เครื่องเก็บขยะอย่างไม่ถูกต้องหรือไม่? ถ้าเป็นเช่นนั้นเราจะทำให้วัตถุ Interop ตายได้อย่างไร? นอกจากนี้ถ้ามันไม่ได้หมายความว่าจะต้องใช้เช่นนี้ทำไมเป็นGC's Collectวิธีแม้Public?


3
สิ่งนี้จะทำให้เกิดคำถาม StackOverflow ใหม่ที่ยอดเยี่ยมเช่น: วิธีการลบอินสแตนซ์ COM โดยไม่ต้องเรียก GC เกี่ยวกับการอ้างอิงแบบวงกลมที่ไม่มีการจัดการโดยเฉพาะ เป็นความท้าทายอย่างหนึ่งที่ทำให้ฉันต้องระมัดระวังในการอัปเกรดโปรแกรมเสริม VB6 Outlook เป็น C # (เราทำงานหลายอย่างเพื่อพัฒนารูปแบบการเข้ารหัสและกรณีทดสอบในฝั่ง VB ที่รับประกันว่าการอ้างอิง COM ถูกฆ่าตายในรูปแบบที่กำหนดขึ้นเมื่อไม่จำเป็นอีกต่อไป)
rkagerer

2
หากสิ่งนี้ใช้กับวัตถุ COM โดยทั่วไปอาจเป็นสถานการณ์ที่ถูกต้อง แต่ฉันขอบอกว่าปัญหาน่าจะเกิดจากคุณกำลังใช้แอปพลิเคชันไคลเอนต์ที่ออกแบบมาสำหรับเดสก์ท็อปแบบโต้ตอบเป็นเซิร์ฟเวอร์ COM จากฐานความรู้ MSDN: "ขณะนี้ Microsoft ไม่แนะนำและไม่สนับสนุนการทำงานอัตโนมัติของแอปพลิเคชัน Microsoft Office จากแอปพลิเคชันไคลเอ็นต์หรือส่วนประกอบที่ไม่ต้องดูแลและไม่โต้ตอบใด ๆ (รวมถึง ASP, ASP.NET, DCOM และ NT Services) เนื่องจาก Office อาจแสดงพฤติกรรมที่ไม่เสถียรและ / หรือการหยุดชะงักเมื่อ Office ทำงานในสภาพแวดล้อมนี้ "
The Dag

2
@TheDag - Microsoft อาจไม่แนะนำ แต่พวกเราหลายคนต้องพอร์ตรหัส VB6 เก่าพร้อมกับ office interop ไปยังแอพ. Net windows ฉันใช้เวลาหลายเดือนในการทำงานจนในที่สุดฉันก็กำจัดการอ้างอิงแขวนที่มองไม่เห็นทั้งหมดสำหรับโครงการแปลง VB6 เป็น. Net ขนาดใหญ่ การเรียนรู้ที่จะปล่อยตามลำดับการมอบหมายแบบย้อนกลับและถือการอ้างอิงในพื้นที่ไปยังวัตถุ com ทุกชิ้นรวมถึงคอลเล็กชันช่วยด้วย
Dib

15

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

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

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

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

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

สิ่งสำคัญคือต้องทราบว่า GC นั้นแตกต่างกันบนเซิร์ฟเวอร์มากกว่าเวิร์กสเตชัน ฉันเคยเห็นปัญหาเล็ก ๆ น้อย ๆ ที่ยากต่อการติดตามปัญหากับคนที่ไม่ได้ทดสอบทั้งสองคน (หรือไม่รู้ด้วยซ้ำว่าพวกเขาเป็นสองคน)

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


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

5
จะขันสกรูทั้งเครื่องได้อย่างไร?
Adam

13

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


12

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


10

สาเหตุใหญ่ประการหนึ่งในการเรียก GC.Collect () คือเมื่อคุณเพิ่งดำเนินการเหตุการณ์สำคัญซึ่งสร้างขยะจำนวนมากเช่นสิ่งที่คุณอธิบาย การโทรหา GC.Collect () เป็นความคิดที่ดีที่นี่ มิฉะนั้น GC อาจไม่เข้าใจว่าเป็นเหตุการณ์ 'ครั้งเดียว'

แน่นอนคุณควรกำหนดโปรไฟล์และดูด้วยตัวคุณเอง


9

เห็นได้ชัดว่าคุณไม่ควรเขียนโค้ดด้วยข้อกำหนดแบบเรียลไทม์ในภาษาที่มีการรวบรวมขยะแบบไม่เรียลไทม์

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


จริง. แต่การทดสอบอัตโนมัติที่สามารถตรวจจับเงื่อนไขข้อผิดพลาด "ออบเจ็กต์ไม่มีสิทธิ์สำหรับการรวบรวมขยะ แต่ควรเป็น" จะมีค่า ฉันนี่อาจทำได้โดยการรวมกันของตรรกะโรงงานตรรกะตัวทำลายและ GC รวบรวม เช่นคลาสเอนทิตีของคุณมีคุณสมบัติ IObjectTracker โดยปกติจะเป็นค่าว่าง แต่ถูกกำหนดโดยโรงงานเอนทิตีวัตถุประสงค์การทดสอบ โรงงานยังแจ้งตัวติดตามการเกิดของวัตถุในขณะที่ผู้ทำลายจะแจ้งการเสียชีวิต (เมื่อมี) หากคุณสามารถทราบว่า "destructor ได้ดำเนินการสำหรับวัตถุที่เก็บขยะทั้งหมด" คุณสามารถตรวจสอบสถานะตัวติดตามเพื่อตรวจจับการรั่วไหล
The Dag

7

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


2
คุณไม่เพียง แต่ทำให้สแตกเดินเท่านั้น แต่เธรดหลักของแอปพลิเคชันของคุณ (และเธรดลูกที่สร้างขึ้น) จะถูกตรึงเพื่อให้ GC สามารถเดินสแต็กได้ ยิ่งแอปของคุณใช้เวลาใน GC นานเท่าใดแอปก็จะยิ่งค้างมากขึ้นเท่านั้น
Scott Dorman

3
ฉันกังวลเกี่ยวกับ App Crash เนื่องจากข้อยกเว้นของหน่วยความจำไม่เพียงพอมากกว่าประสิทธิภาพที่ช้าเนื่องจากแอป / GC ทิ้งสิ่งที่ไม่จำเป็นอีกต่อไป มีใครทราบบ้างว่าเหตุใด Microsoft จึงทิ้งข้อยกเว้น OOM โดยที่ FIRST ทิ้งขยะ (หากไม่มีขั้นตอน OBVIOUS นี้ - หรืออย่างน้อยคำอธิบายว่าเหตุใดขั้นตอนนี้จึงไม่ถูกพยายามก่อนที่จะโยนข้อยกเว้น OOM ฉันไม่แน่ใจว่าฉันมีความเชื่อในสิ่งที่เกิดขึ้น "โดยอัตโนมัติ" ตามที่ควรจะเป็น "
Wonderbird

6

Infact ฉันไม่คิดว่าการเรียก GC ว่าเป็นวิธีปฏิบัติที่แย่มาก
อาจมีบางกรณีที่เราต้องการสิ่งนั้น ตัวอย่างเช่นฉันมีแบบฟอร์มที่รันเธรดซึ่งอินเทิร์นจะเปิดตารางที่แตกต่างกันในฐานข้อมูลแยกเนื้อหาในฟิลด์ BLOB ไปยังไฟล์ temp เข้ารหัสไฟล์จากนั้นอ่านไฟล์ในไบนารีสตรีมและกลับเข้าสู่ BLOB ในตารางอื่น

การดำเนินการทั้งหมดใช้หน่วยความจำค่อนข้างมากและไม่แน่ใจเกี่ยวกับจำนวนแถวและขนาดของเนื้อหาไฟล์ในตาราง

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

หลังจากนี้คิดว่าทำงานได้ดีอย่างน้อยที่สุด !!!
ฉันโทรด้วยวิธีต่อไปนี้:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).

5

ภายใต้. net เวลาที่ต้องใช้ในการรวบรวมขยะมีความสัมพันธ์อย่างมากกับปริมาณสิ่งของที่ไม่ใช่ขยะมากกว่าจำนวนสิ่งของที่เป็นอยู่ อันที่จริงเว้นแต่วัตถุจะแทนที่Finalize(อย่างชัดเจนหรือผ่านตัวทำลาย C #) เป็นเป้าหมายของ a WeakReferenceอยู่บน Large Object Heap หรือมีความพิเศษในวิธีอื่น ๆ ที่เกี่ยวข้องกับ gc สิ่งเดียวที่ระบุหน่วยความจำที่อยู่ ในฐานะที่เป็นวัตถุคือการมีอยู่ของการอ้างอิงที่ฝังรากถึงมัน มิฉะนั้นการดำเนินการของ GC จะคล้ายคลึงกับการสร้างทุกสิ่งที่มีคุณค่าและทำให้อาคารมีชีวิตชีวาสร้างอาคารใหม่บนไซต์ของอาคารเก่าและวางสิ่งของมีค่าทั้งหมดไว้ในนั้น ความพยายามที่ต้องใช้ในการระเบิดอาคารนั้นไม่ขึ้นอยู่กับปริมาณขยะภายในอาคาร

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

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


5

การสร้างภาพแบบวนซ้ำ - แม้ว่าคุณจะเรียก Dispose แต่หน่วยความจำจะไม่ถูกกู้คืน เก็บขยะทุกครั้ง. ฉันเปลี่ยนจากหน่วยความจำ 1.7GB ในแอปประมวลผลภาพเป็น 24MB และประสิทธิภาพก็ยอดเยี่ยม

มีเวลาที่คุณต้องโทรหา GC.Collect


2
โทรDisposeถูกไม่ควรจะหน่วยความจำการเปิดตัวการจัดการ ดูเหมือนคุณจะไม่รู้ว่าโมเดลหน่วยความจำใน. NET ทำงานอย่างไร
Andrew Barber

4

เรามีปัญหาคล้ายกันกับคนเก็บขยะไม่เก็บขยะและทำให้หน่วยความจำว่าง

ในโปรแกรมของเราเรากำลังประมวลผลสเปรดชีต Excel ขนาดพอประมาณด้วย OpenXML สเปรดชีตมีตั้งแต่ 5 ถึง 10 "แผ่นงาน" โดยมีประมาณ 1,000 แถวจาก 14 คอลัมน์

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

เราพบหนึ่ง

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

การเรียก GC จากภายในรูทีนย่อยไม่ได้ผล ความทรงจำไม่เคยถูกเรียกคืน ...

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

โดยการย้ายการเรียก GC ไปนอกขอบเขตของรูทีนย่อยขยะจะถูกรวบรวมและหน่วยความจำจะถูกปลดปล่อย

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

ฉันหวังว่านี้จะช่วยให้คนอื่น ๆ ที่กำลังผิดหวังกับคอลเลกชัน .NET GC.Collect()ขยะเมื่อมันดูเหมือนจะไม่สนใจสายไป

พอลสมิ ธ


4

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

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

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

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


2

ฉันคิดว่าคุณพูดถูกเกี่ยวกับสถานการณ์นี้ แต่ฉันไม่แน่ใจเกี่ยวกับ API

Microsoft กล่าวว่าในกรณีเช่นนี้คุณควรเพิ่มความดันหน่วยความจำเพื่อเป็นคำใบ้ให้กับ GC ว่าเร็ว ๆ นี้ควรดำเนินการรวบรวม


2
น่าสนใจ แต่เอกสารระบุว่าควรใช้ AddMemoryPressure เมื่อ 'วัตถุที่มีการจัดการขนาดเล็กจัดสรรหน่วยความจำ UN ที่มีการจัดการจำนวนมาก' (เน้นของฉัน)
Robert Paulson

2

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


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

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

2

ความปรารถนาที่จะโทรหา GC.Collect () มักจะพยายามปกปิดข้อผิดพลาดที่คุณทำที่อื่น!

จะดีกว่าถ้าคุณพบว่าลืมทิ้งสิ่งของที่คุณไม่ต้องการอีกต่อไป


5
นั่นอาจเป็นลักษณะทั่วไป
MickyD

1

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


1

NET Framework เองไม่เคยออกแบบมาให้ทำงานในสภาพแวดล้อมแบบเรียลไทม์ หากคุณต้องการการประมวลผลแบบเรียลไทม์จริงๆคุณจะใช้ภาษาเรียลไทม์แบบฝังที่ไม่ได้ใช้. NET หรือใช้. NET Compact Framework ที่ทำงานบนอุปกรณ์ Windows CE


เขาสามารถใช้. Net Micro Framework ซึ่งออกแบบมาสำหรับสภาพแวดล้อมแบบเรียลไทม์
TraumaPony

@TraumaPony: ตรวจสอบแผนภูมิที่ด้านล่างของหน้านี้msdn.microsoft.com/en-us/embedded/bb278106.aspx : เห็นได้ชัดว่า Micro Framework ไม่ได้ออกแบบมาสำหรับสภาพแวดล้อมแบบเรียลไทม์ อย่างไรก็ตามได้รับการออกแบบมาสำหรับสภาพแวดล้อมแบบฝัง (เช่น WinCE) แต่มีความต้องการพลังงานที่ต่ำกว่า
Scott Dorman

1

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

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

ในทั้งสองกรณีผลลัพธ์นั้นง่าย: ไม่มี GC.Collect, หน่วยความจำไม่เพียงพอ, สม่ำเสมอ; GC รวบรวมประสิทธิภาพที่ไร้ที่ติ

ฉันพยายามแก้ปัญหาหน่วยความจำหลายครั้งแล้ว แต่ก็ไม่เกิดประโยชน์ ฉันเอามันออก

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

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

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