เมื่อไหร่ที่จะยอมรับ GC.Collect


166

คำแนะนำทั่วไปคือคุณไม่ควรโทรGC.Collectจากรหัสของคุณ แต่มีข้อยกเว้นอะไรสำหรับกฎนี้

ฉันสามารถนึกถึงกรณีเฉพาะบางกรณีที่มันอาจสมเหตุสมผลในการบังคับให้เก็บขยะ

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

มีกรณีอื่น ๆ ที่เป็นที่ยอมรับที่จะเรียกGC.Collect?


คำตอบ:


153

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

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

อัพเดท 2.7.2018

ในฐานะของ .NET 4.5 - มีและGCLatencyMode.LowLatency เมื่อเข้าและออกจากทั้งสองโหมดเหล่านี้ก็จะแนะนำว่าคุณบังคับให้ประชาคมโลกที่เต็มไปด้วยGCLatencyMode.SustainedLowLatencyGC.Collect(2, GCCollectionMode.Forced)

ตั้งแต่. NET 4.6 - มีGC.TryStartNoGCRegionวิธีการ (ใช้เพื่อตั้งค่าอ่านอย่างเดียวGCLatencyMode.NoGCRegion) ตัวเองสามารถดำเนินการรวบรวมขยะเต็มบล็อกในความพยายามที่จะเพิ่มหน่วยความจำเพียงพอ แต่เนื่องจากเราไม่อนุญาต GC ระยะเวลาฉันจะยืนยันว่ามันเป็นความคิดที่ดีในการดำเนินการ GC เต็มรูปแบบก่อนและหลัง

ที่มา: วิศวกร Microsoft Ben Watson: การเขียนโค้ด. NET ประสิทธิภาพสูง , 2nd Ed 2018

ดู:


8
ตามซอร์สโค้ด MS เรียก GC.Collect (2) ทุก 850 มิลลิวินาทีก็โอเค ไม่เชื่อเหรอ จากนั้นเพียงแค่ดู PresentationCore.dll, MS.Internal.MemoryPressure.ProcessAdd () ขณะนี้ฉันมีแอปประมวลผลรูปภาพ (รูปภาพขนาดเล็กไม่มีสิ่งใดที่มีแรงกดดันหน่วยความจำจริง) ที่เรียก GC.Collect (2) ใช้เวลานานกว่า 850ms ดังนั้นแอปทั้งหมดจึงหยุดการทำงานนี้ (แอพใช้เวลา 99.7% ใน GC)
springy76

36
@ springy76: ถูกทำโดยไมโครซอฟท์ในสถานที่หนึ่งไม่ได้หมายความว่ามันถือว่าเป็นสิ่งที่ดีโดยผู้ที่ให้คำแนะนำจากไมโครซอฟท์ ...
จอนสกีต

4
ฉันไม่ชอบตัวอย่างนั้น Poing ทำอะไรหลังจากปิดฟอร์ม? ตัวอย่างที่ดีอย่างหนึ่งที่ฉันเห็นคือหลังจากโหลดระดับเกมบน XBox หรือ WindowsPhone บนแพลตฟอร์มเหล่านั้น GC จะทำงานหลังจากจัดสรร 1MB หรือ sth เช่นนั้น ดังนั้นจึงเป็นการดีที่จะจัดสรรให้มากที่สุดเท่าที่จะทำได้ระหว่างการโหลดระดับ (ขณะที่แสดงหน้าจอสแปลชบางส่วน) จากนั้นทำ GC.Collect เพื่อพยายามหลีกเลี่ยงการสะสมในระหว่างเกม
Piotr Perak

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

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

50

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

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestA(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
TestB(); // may allocate lots of transient objects
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
...

ดังนั้นTestA()และTestB()เรียกใช้ด้วยสถานะที่ใกล้เคียงที่สุด - นั่นคือTestB()ไม่ได้รับการตอกเพียงเพราะTestAปล่อยให้มันอยู่ใกล้กับจุดเปลี่ยน

ตัวอย่างคลาสสิกจะเป็น exe ง่ายคอนโซล (เป็นMainวิธีการเรียงลำดับ-พอที่จะโพสต์ที่นี่เช่น) แสดงให้เห็นว่าความแตกต่างระหว่าง concatenation StringBuilderสตริงคล้องและ

ถ้าฉันต้องการบางสิ่งบางอย่างที่แม่นยำนี่จะเป็นการทดสอบอิสระที่สมบูรณ์แบบสองครั้ง - แต่บ่อยครั้งนี่ก็เพียงพอแล้วหากเราต้องการลด GC (หรือทำให้ปกติ) ในระหว่างการทดสอบเพื่อลดความรู้สึกหยาบสำหรับพฤติกรรม

ในระหว่างการผลิตรหัส? ฉันยังไม่ได้ใช้ ;-p


1
และฉันอาจเพิ่ม "WaitForPendingFinalizers" (หรืออะไรก็ตาม) ในกรณีนี้เช่นกัน ;-p
Marc Gravell

29

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

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

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

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

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

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

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

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

ดูเพิ่มเติม " Tidbits ประสิทธิภาพของ Rico Mariani "



11

ในระบบขนาดใหญ่ 24/7 หรือ 24/6 - ระบบที่ตอบสนองต่อข้อความ RPC ร้องขอหรือสำรวจฐานข้อมูลหรือกระบวนการอย่างต่อเนื่องซึ่งมีประโยชน์ในการระบุการรั่วไหลของหน่วยความจำ สำหรับสิ่งนี้ฉันมักจะเพิ่มกลไกในแอปพลิเคชันเพื่อหยุดการประมวลผลชั่วคราวแล้วดำเนินการรวบรวมขยะแบบเต็ม สิ่งนี้ทำให้ระบบอยู่ในสถานะที่นิ่งซึ่งหน่วยความจำที่เหลืออยู่เป็นหน่วยความจำที่มีอายุการใช้งานที่ถูกต้องตามกฎหมาย (แคช, การกำหนดค่า, & c.) หรืออื่น ๆ คือ 'รั่วไหล' (วัตถุที่ไม่ได้คาดหวัง

การมีกลไกนี้ทำให้การใช้หน่วยความจำโปรไฟล์ทำได้ง่ายขึ้นมากเนื่องจากรายงานจะไม่ถูกปกคลุมด้วยเสียงรบกวนจากการประมวลผลที่ใช้งานอยู่

เพื่อให้แน่ใจว่าคุณได้รับขยะทั้งหมดคุณต้องทำการรวบรวมสองชุด:

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

ในฐานะที่เป็นคอลเลกชันแรกจะทำให้วัตถุใด ๆ ที่มี finalizers ที่จะสรุป (แต่ไม่จริงขยะรวบรวมวัตถุเหล่านี้) GC ตัวที่สองจะขยะรวบรวมวัตถุที่สรุปแล้วเหล่านี้


ฉันเห็นคอลเล็กชันสองรอบในสองแห่งแล้ว แต่หลังจากอ่านข้อความในเอกสาร MSDN สำหรับGC.WaitForPendingFinalizersที่บอกว่า: "รอให้ผู้เข้ารอบสุดท้ายทั้งหมดเสร็จสมบูรณ์ก่อนดำเนินการต่อโดยไม่ต้องเรียก GC.WaitForPendingFinalizers นี้มาก่อน ลูปของผู้ปฏิบัติงานด้านล่างอาจดำเนินการในเวลาเดียวกันกับ finalizers ด้วยการเรียกนี้ลูปของผู้ปฏิบัติงานจะเรียกใช้งานหลังจากมีการเรียก Finalizers ทั้งหมดแล้วเท่านั้น " ฉันแค่หวาดระแวงแตะ คุณรู้แหล่งที่ชัดเจนสำหรับการทำบัตรสองรอบหรือไม่?
jerhewet

1
@ jerhewet: กุญแจสำคัญในการทำความเข้าใจว่าทำไมต้องมีสองคอลเลกชันคือการทำความเข้าใจกับสิ่งที่เกิดขึ้นกับวัตถุที่มี finalizers แต่น่าเสียดายที่ผมไม่ได้มีสิ่งที่คุณกำลังถามหา แต่ได้อ่านของบทความนี้และคำถามนี้ในดังนั้น
Paul Ruane

10

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

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

อย่างไรก็ตามเวลาส่วนใหญ่ของ GC นั้นฉลาดพอที่จะทำสิ่งนี้ได้อยู่ดี


8

ในฐานะที่เป็นโซลูชั่นการกระจายตัวของหน่วยความจำ ฉันกำลังออกจากข้อยกเว้นหน่วยความจำในขณะที่เขียนข้อมูลจำนวนมากลงในสตรีมหน่วยความจำ (อ่านจากสตรีมเครือข่าย) ข้อมูลถูกเขียนเป็น 8K ชิ้น หลังจากไปถึง 128M มีข้อยกเว้นแม้ว่าจะมีหน่วยความจำจำนวนมากที่มีอยู่ (แต่ก็มีการแยกส่วน) การโทร GC.Collect () แก้ไขปัญหา ฉันสามารถจัดการได้มากกว่า 1G หลังจากการแก้ไข


7

ดูบทความนี้โดย Rico Mariani เขาให้สองกฎเมื่อโทร GC.Collect (กฎ 1 คือ: "ไม่"):

เมื่อใดที่จะเรียก GC.Collect ()


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

5

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

เมื่อการเขียนโปรแกรมโดยใช้ Interop วัตถุ COM ทุกชิ้นควรได้รับการปล่อยตัวด้วยตนเองโดยปกติจะใช้ Marshal.ReleseComObject () นอกจากนี้การเรียก Garbage Collection ด้วยตนเองสามารถช่วย "ล้างข้อมูล" ได้เล็กน้อย การเรียกใช้รหัสต่อไปนี้เมื่อคุณทำกับวัตถุ Interop ดูจะช่วยได้ไม่น้อย:

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

ในประสบการณ์ส่วนตัวของฉันโดยใช้การรวม ReleaseComObject และตนเองเรียกเก็บขยะอย่างมากจะช่วยลดการใช้หน่วยความจำของผลิตภัณฑ์ Office โดยเฉพาะ Excel


ใช่ฉันพบว่าด้วย. net เข้าถึง excel ซึ่งทำงานผ่านวัตถุ com ด้วย เป็นสิ่งสำคัญที่จะต้องทราบว่าสิ่งนี้จะไม่ทำงานได้ดีในโหมด DEBUG เนื่องจากการทำงานของ GC นั้นมี จำกัด มันจะทำงานตามที่ตั้งใจเท่านั้นในโหมด RELEASE ลิงค์ที่เกี่ยวข้อง: stackoverflow.com/questions/17130382/…
Blechdose

5

ฉันทำการทดสอบประสิทธิภาพของ array และ list:

private static int count = 100000000;
private static List<int> GetSomeNumbers_List_int()
{
    var lstNumbers = new List<int>();
    for(var i = 1; i <= count; i++)
    {
        lstNumbers.Add(i);
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Array()
{
    var lstNumbers = new int[count];
    for (var i = 1; i <= count; i++)
    {
        lstNumbers[i-1] = i + 1;
    }
    return lstNumbers;
}
private static int[] GetSomeNumbers_Enumerable_Range()
{
    return  Enumerable.Range(1, count).ToArray();
}

static void performance_100_Million()
{
    var sw = new Stopwatch();

    sw.Start();
    var numbers1 = GetSomeNumbers_List_int();
    sw.Stop();
    //numbers1 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"List<int>\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
    var numbers2 = GetSomeNumbers_Array();
    sw.Stop();
    //numbers2 = null;
    //GC.Collect();
    Console.WriteLine(String.Format("\"int[]\" took {0} milliseconds", sw.ElapsedMilliseconds));

    sw.Reset();
    sw.Start();
//getting System.OutOfMemoryException in GetSomeNumbers_Enumerable_Range method
    var numbers3 = GetSomeNumbers_Enumerable_Range();
    sw.Stop();
    //numbers3 = null;
    //GC.Collect();

    Console.WriteLine(String.Format("\"int[]\" Enumerable.Range took {0} milliseconds", sw.ElapsedMilliseconds));
}

และฉันได้OutOfMemoryExceptionในวิธี GetSomeNumbers_Enumerable_Range วิธีแก้ปัญหาเดียวคือการจัดสรรคืนหน่วยความจำโดย:

numbers = null;
GC.Collect();

ทำไมต้องลงคะแนน คำตอบของฉันเป็นตัวอย่างที่แสดงให้เห็นว่าเมื่อใดที่จะโทรหา GC คุณมีข้อเสนอแนะที่ดีกว่านี้หรือไม่?
Daniel B

4

ในตัวอย่างของคุณฉันคิดว่าการโทร GC.Collect ไม่ใช่ปัญหา แต่เป็นปัญหาการออกแบบ

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

ด้วยวิธีนี้คุณไม่ต้องกังวลกับการโทร GC.Collect (ซึ่งคุณไม่ควรทำบ่อยนัก )

ที่ถูกกล่าวว่า Rico Mariani มีโพสต์บล็อกที่ยอดเยี่ยมในเรื่องนี้ซึ่งสามารถพบได้ที่นี่:

http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx


3

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

ตัวอย่างเช่นฉันมีการทดสอบบางอย่างเช่น:

WeakReference w = CodeThatShouldNotMemoryLeak();
Assert.IsTrue(w.IsAlive);
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsFalse(w.IsAlive);

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




2
using(var stream = new MemoryStream())
{
   bitmap.Save(stream, ImageFormat.Png);
   techObject.Last().Image = Image.FromStream(stream);
   bitmap.Dispose();

   // Without this code, I had an OutOfMemory exception.
   GC.Collect();
   GC.WaitForPendingFinalizers();
   //
}

2

มีบางสถานการณ์ที่ปลอดภัยกว่าดีกว่าขออภัย

นี่คือสถานการณ์หนึ่ง

เป็นไปได้ที่จะเขียนDLL ที่ไม่มีการจัดการใน C # โดยใช้ IL rewrites (เนื่องจากมีสถานการณ์ที่จำเป็น)

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

ปลอดภัยกว่าดีกว่าขออภัย


2

ฉันยังค่อนข้างไม่แน่ใจเกี่ยวกับเรื่องนี้ ฉันทำงานมาแล้ว 7 ปีใน Application Server การติดตั้งที่ใหญ่กว่าของเราใช้ประโยชน์จาก 24 GB Ram Multithreaded อย่างสูงและทุกสายเรียกร้องให้ GC.Collect () พบปัญหาประสิทธิภาพที่แย่มาก

คอมโพเนนต์ของบุคคลที่สามหลายแห่งใช้ GC.Collect () เมื่อพวกเขาคิดว่ามันฉลาดที่จะทำสิ่งนี้ในตอนนี้ ดังนั้นรายงาน Excel ที่เรียบง่ายจึงบล็อก App Server สำหรับเธรดทั้งหมดหลายครั้งต่อนาที

เราต้องปรับโครงสร้างองค์ประกอบบุคคลที่สามทั้งหมดเพื่อที่จะลบการเรียก GC.Collect () และทำงานได้ดีหลังจากทำเช่นนี้

แต่ฉันใช้เซิร์ฟเวอร์บน Win32 เช่นกันและที่นี่ฉันเริ่มใช้ GC.Collect () อย่างหนักหลังจากได้รับ OutOfMemoryException

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

สิ่งหนึ่งที่ฉันสงสัยคือ OOM Exception นั้น ... ถ้าฉันจะเขียน. Net Framework และฉันไม่สามารถจัดสรรบล็อกหน่วยความจำฉันจะใช้ GC.Collect (), หน่วยความจำ Defrag (??) ลองอีกครั้ง และถ้าฉันยังหาบล็อกหน่วยความจำว่างไม่ได้ฉันก็จะโยน OOM-Exception

หรืออย่างน้อยก็ทำให้พฤติกรรมนี้เป็นตัวเลือกที่กำหนดค่าได้เนื่องจากข้อเสียของปัญหาประสิทธิภาพด้วย GC.Collect

ตอนนี้ฉันมีรหัสจำนวนมากเช่นนี้ในแอปของฉันเพื่อ "แก้ปัญหา" ปัญหา:

public static TResult ExecuteOOMAware<T1, T2, TResult>(Func<T1,T2 ,TResult> func, T1 a1, T2 a2)
{

    int oomCounter = 0;
    int maxOOMRetries = 10;
    do
    {
        try
        {
            return func(a1, a2);
        }
        catch (OutOfMemoryException)
        {
            oomCounter++;
            if (maxOOMRetries > 10)
            {
                throw;
            }
            else
            {
                Log.Info("OutOfMemory-Exception caught, Trying to fix. Counter: " + oomCounter.ToString());
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(oomCounter * 10));
                GC.Collect();
            }
        }
    } while (oomCounter < maxOOMRetries);

    // never gets hitted.
    return default(TResult);
}

(โปรดทราบว่าลักษณะการทำงานของ Thread.Sleep () เป็นลักษณะการทำงาน apecific จริง ๆ เนื่องจากเรากำลังเรียกใช้บริการแคช ORM และบริการใช้เวลาในการปล่อยวัตถุแคชทั้งหมดถ้า RAM มีค่าที่กำหนดไว้ล่วงหน้าดังนั้นจึงรอ ไม่กี่วินาทีในครั้งแรกและเพิ่มเวลาการรอแต่ละครั้งที่เกิดขึ้นของ OOM)


GC.Collectชิ้นส่วนที่ไม่ควรเรียก เนื่องจากมีผลกว้างแอปพลิเคชันเท่านั้นแอปพลิเคชันควรทำเช่นนั้น (ถ้าเลย)
CodesInChaos

If i would have written the .Net Framework, and i can't alloc a memory block, i would use GC.Collect(),- ฉันคิดว่าพวกเขากำลังทำสิ่งนี้อยู่แล้ว - ฉันเห็นสิ่งบ่งชี้ว่าหนึ่งในทริกเกอร์ GC ภายในเป็นความล้มเหลวบางอย่างในการจัดสรรหน่วยความจำ
G. Stoynev

2

คุณควรพยายามหลีกเลี่ยงการใช้ GC.Collect () เนื่องจากมีราคาแพงมาก นี่คือตัวอย่าง:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet.Remove(RecordSet[0]);

        }
        GC.Collect(); // AVOID
    }

ผลการทดสอบ: การใช้งาน CPU 12%

เมื่อคุณเปลี่ยนเป็น:

        public void ClearFrame(ulong timeStamp)
    {
        if (RecordSet.Count <= 0) return;
        if (Limit == false)
        {
            var seconds = (timeStamp - RecordSet[0].TimeStamp)/1000;
            if (seconds <= _preFramesTime) return;
            Limit = true;
            do
            {
                RecordSet[0].Dispose(); //  Bitmap destroyed!
                RecordSet.Remove(RecordSet[0]);
            } while (((timeStamp - RecordSet[0].TimeStamp) / 1000) > _preFramesTime);
        }
        else
        {
            RecordSet[0].Dispose(); //  Bitmap destroyed!
            RecordSet.Remove(RecordSet[0]);

        }
        //GC.Collect();
    }

ผลการทดสอบ: การใช้งาน CPU 2-3%


1

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

การเรียก SerialPort.Close () บนพอร์ต Call Dispose () บนวัตถุ แต่ยังคงอยู่ในหน่วยความจำจนกว่าการรวบรวมขยะจะทำงานจริงทำให้รีจิสทรียังคงค้างอยู่จนกว่าตัวรวบรวมขยะจะตัดสินใจปล่อยทรัพยากร

จากhttps://stackoverflow.com/a/58810699/8685342 :

try
{
    if (port != null)
        port.Close(); //this will throw an exception if the port was unplugged
}
catch (Exception ex) //of type 'System.IO.IOException'
{
    System.GC.Collect();
    System.GC.WaitForPendingFinalizers();
}

port = null;

0

สิ่งนี้ไม่เกี่ยวข้องกับคำถาม แต่สำหรับการแปลง XSLT ใน. NET (XSLCompiledTranform) ดังนั้นคุณอาจไม่มีทางเลือก ตัวเลือกอื่นคือตัวควบคุม MSHTML


0

หากคุณใช้. net เวอร์ชันน้อยกว่า 4.5 การรวบรวมด้วยตนเองอาจหลีกเลี่ยงไม่ได้ (โดยเฉพาะถ้าคุณกำลังจัดการกับ 'วัตถุขนาดใหญ่' จำนวนมาก)

ลิงก์นี้อธิบายสาเหตุ:

https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/


0

เหตุผลหนึ่งที่ดีสำหรับการโทร GC คือบนคอมพิวเตอร์ ARM ขนาดเล็กที่มีหน่วยความจำน้อยเช่น Raspberry PI (ทำงานกับโมโน) หากแฟรกเมนต์หน่วยความจำที่ไม่ได้จัดสรรใช้ RAM ของระบบมากเกินไประบบปฏิบัติการ Linux อาจไม่เสถียร ฉันมีแอปพลิเคชันที่ฉันต้องโทรหา GC ทุก ๆ วินาที (!) เพื่อกำจัดปัญหาหน่วยความจำล้น

อีกวิธีที่ดีคือการกำจัดวัตถุเมื่อไม่ต้องการอีกต่อไป น่าเสียดายที่นี่ไม่ใช่เรื่องง่ายในหลาย ๆ กรณี


0

เนื่องจากมี Small object heap (SOH) และ Large object heap (LOH)

เราสามารถเรียก GC.Collect () เพื่อลบวัตถุอ้างอิงใน SOP และย้ายวัตถุที่มีชีวิตไปยังรุ่นต่อไป

ใน. net4.5 เราสามารถกระชับ LOH โดยใช้largeobjectheapcompactionmode


0

หากคุณกำลังสร้างSystem.Drawing.Bitmapวัตถุใหม่จำนวนมากตัวเก็บรวบรวมขยะไม่ได้ล้างออก ในที่สุด GDI + จะคิดว่าคุณมีหน่วยความจำไม่เพียงพอและจะโยนข้อยกเว้น "พารามิเตอร์ไม่ถูกต้อง" การโทรGC.Collect()ทุกครั้ง (ไม่บ่อยเกินไป!) ดูเหมือนจะแก้ไขปัญหานี้ได้

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