เมื่อใดที่ฉันควรใช้ GC.SuppressFinalize ()


287

ใน. NET ฉันควรใช้ภายใต้สถานการณ์GC.SuppressFinalize()ใด

การใช้วิธีนี้ทำให้ฉันได้เปรียบอะไร


ฉันได้เห็นคำถามสองสามข้อเกี่ยวกับ finalizers และ IDisposable, stackoverflow ควรมีบางสิ่งบางอย่างเกี่ยวกับ GC.SupressFinalize และการอ้างอิงที่อ่อนแอ
Sam Saffron

ฉันไม่คิดว่าการอ้างอิงที่อ่อนแอจะทำอะไรได้มากนักเกี่ยวกับการเข้ารอบสุดท้าย - บางทีคุณควรโพสต์คำถามตรงๆเพิ่มเติมเกี่ยวกับพวกเขา
Michael Burr

ใช่ฉันหมายถึงการโพสต์คำถามแยกต่างหากเกี่ยวกับการอ้างอิงอ่อน ๆ ทั้งหมดนี้สามารถผูกเข้าด้วยกันเมื่อคุณสร้างกลุ่มวัตถุ นอกจากนี้ฉันควรถามคำถามเกี่ยวกับการฟื้นฟูวัตถุ ala ReRegisterForFinalize
Sam Saffron

คำตอบ:


296

SuppressFinalizeควรถูกเรียกโดยคลาสที่มี finalizer เท่านั้น กำลังแจ้ง Garbage Collector (GC) ว่าthisมีการล้างวัตถุอย่างสมบูรณ์

IDisposableรูปแบบที่แนะนำเมื่อคุณมี finalizer คือ:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

โดยปกติ CLR จะเก็บแท็บบนวัตถุด้วยตัวสร้างขั้นสุดท้ายเมื่อมันถูกสร้างขึ้น (ทำให้มีราคาแพงกว่าในการสร้าง) SuppressFinalizeบอก GC ว่าวัตถุได้รับการทำความสะอาดอย่างถูกต้องและไม่จำเป็นต้องไปยังคิวสุดท้าย ดูเหมือนว่า destructor ของ C ++ แต่ไม่ได้ทำอะไรคล้าย ๆ กัน

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

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

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

หมายเหตุ: บางครั้งผู้ทำโคเดอร์จะเพิ่ม finalizer เพื่อตรวจแก้จุดบกพร่องการสร้างIDisposableคลาสของตนเองเพื่อทดสอบว่ารหัสกำจัดIDisposableวัตถุอย่างเหมาะสม

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif

1
ในตัวอย่างโค้ดแรกฉันเพิ่งโพสต์ว่ารูปแบบ IDisposable + finalizer ที่แนะนำมีลักษณะอย่างไร การดีบักรหัสนั้นดี แต่มันสามารถเบี่ยงเบนความสนใจได้ .. ฉันสามารถแนะนำให้หลีกเลี่ยง finalizers ยกเว้นคลาสที่มีทรัพยากรที่ไม่มีการจัดการ การเขียนรหัส finalizer ที่ปลอดภัยนั้นไม่ใช่เรื่องเล็กน้อย
Robert Paulson

1
สวัสดีทำไมเราต้องเรียกการกำจัดด้วย false เป็นพารามิเตอร์จาก finalizer? เกิดอะไรขึ้นถ้าการกำจัดไม่เคยถูกเรียกแล้วจะไม่กำจัด? จะเกิดอะไรขึ้นถ้าเราแค่ตรวจสอบว่าวัตถุถูกกำจัดหรือไม่และทำการล้างข้อมูลจริง
ฝัน

3
@Dreamer - มันขึ้นอยู่กับการใช้งานของคุณ โดยทั่วไปคุณต้องการทราบว่าการกำจัดถูกเรียกใช้โดย finalizer เมื่อเทียบกับการใช้งาน IDisposable.Dispose () หากถูกเรียกจากผู้เข้ารอบสุดท้ายคุณต้องถือว่าการอ้างอิงส่วนตัวไม่ถูกต้องอีกต่อไปและคุณไม่สามารถทำอะไรได้มากนัก หากเรียกจาก IDisposable.Dispose () คุณรู้ว่าการอ้างอิงนั้นยังคงใช้ได้
Robert Paulson

32
ถ้าชั้นการดำเนินการIDisposableไม่ได้sealedแล้วมันควรจะรวมถึงการเรียกร้องให้GC.SuppressFinalize(this) ถึงแม้ว่ามันจะไม่รวมถึง finalizer นี่เป็นสิ่งจำเป็นเพื่อให้แน่ใจว่าความหมายที่ถูกต้องสำหรับประเภทที่ได้รับซึ่งเพิ่ม finalizer ที่ผู้ใช้กำหนด แต่จะแทนที่เฉพาะDispose(bool)วิธีที่ได้รับการป้องกัน
Sam Harwell

1
ไม่ได้ถูกsealedกล่าวถึงโดย @SamHarwell เป็นสิ่งสำคัญสำหรับคลาสที่ได้รับ ผลการ CodeAnalysis ใน ca1816 ca1063 + เมื่อเรียนไม่ได้ปิดผนึก SuppressFinalizeแต่การเรียนที่ปิดสนิทโดยไม่ต้องมีการปรับ
dashesy

38

SupressFinalizeแจ้งให้ระบบทราบว่าสิ่งใดก็ตามที่จะต้องทำใน Finalizer นั้นเสร็จสิ้นแล้วดังนั้น Finalizer จึงไม่จำเป็นต้องถูกเรียก จาก. NET docs:

วัตถุที่ใช้อินเทอร์เฟซ IDisposable สามารถเรียกวิธีนี้จากนั้น IDisposable.Dispose วิธีการเพื่อป้องกันการเก็บขยะจากการเรียก Object.Finalize บนวัตถุที่ไม่ต้องการ

โดยทั่วไปแล้วDispose()วิธีการส่วนใหญ่ควรจะสามารถโทรGC.SupressFinalize()ได้เพราะมันควรจะล้างข้อมูลทุกอย่างที่จะต้องทำความสะอาดใน Finalizer

SupressFinalizeเป็นเพียงสิ่งที่ให้การเพิ่มประสิทธิภาพที่ช่วยให้ระบบไม่รบกวนการจัดคิววัตถุไปยังเธรด finalizer ที่เขียนถูกต้องDispose()/ finalizer GC.SupressFinalize()ควรทำงานอย่างถูกต้องโดยมีหรือไม่มีการเรียกไปยัง


2

ต้องเรียกใช้Disposeเมธอดนั้นกับวิธีการของออบเจ็กต์ที่ใช้งานIDisposableด้วยวิธีนี้ GC จะไม่เรียก Finalizer อีกครั้งหากมีคนเรียกDisposeเมธอด

โปรดดู: GC.SuppressFinalize (Object) วิธี - Microsoft Docs


9
ฉันคิดว่า "ต้อง" ผิด - ไม่ใช่แม้แต่ "ควร" - เป็นเพียงแค่นั้นในบางสถานการณ์คุณสามารถกำจัดค่าใช้จ่ายในการจัดคิว / จบวัตถุได้
พื้นฐาน

1
Dispose(true);
GC.SuppressFinalize(this);

หากวัตถุมี finalizer, .net ใส่การอ้างอิงในคิวการสรุป

เนื่องจากเรามีการโทรDispose(ture)เป็นวัตถุที่ชัดเจนดังนั้นเราจึงไม่ต้องการคิวการสรุปเพื่อทำงานนี้

ดังนั้นการโทรGC.SuppressFinalize(this)ลบการอ้างอิงในคิวการสรุป


0

ถ้าชั้นเรียนหรืออะไรที่ได้มาจากมันอาจถืออ้างอิงสดที่ผ่านมาเพื่อวัตถุที่มี finalizer แล้วอย่างใดอย่างหนึ่งGC.SuppressFinalize(this)หรือGC.KeepAlive(this)ควรจะเรียกว่าบนวัตถุหลังจากการดำเนินการใด ๆ ที่อาจได้รับผลกระทบโดย finalizer ว่าจึงมั่นใจได้ว่าวอน finalizer ไม่ทำงานจนกว่าจะเสร็จสิ้นการดำเนินการ

ค่าใช้จ่ายของGC.KeepAlive()และGC.SuppressFinalize(this)เป็นหลักเหมือนกันในคลาสใด ๆ ที่ไม่มี finalizer และคลาสที่มี finalizers ควรเรียกโดยทั่วไปGC.SuppressFinalize(this)ดังนั้นการใช้ฟังก์ชันหลังเนื่องจากขั้นตอนสุดท้ายของDispose()อาจไม่จำเป็นเสมอไป แต่จะไม่จำเป็น จะผิด.

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