Finalizer ไม่ถูกเรียกหลังจาก GC.Collect () [ซ้ำ]

Aug 17 2020

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

เฉพาะเมื่อ Main จบลงเครื่องสุดท้ายจะถูกตี แต่น่าแปลกใจที่มันถูกตีสองครั้ง ฉันขาดอะไรไป?

class Program
{
    public static void Main(string[] args)
    {
        SomeClass some = new SomeClass("Hello World!");
        some = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

class SomeClass 
{
    string ss;
    public SomeClass(string s) { ss = s; }
    ~SomeClass()
    {
        var hash = this.GetHashCode();
    }
}

ภาคผนวกมีความแตกต่างในการรันโปรแกรมในโหมดดีบักกับโหมดรีลีส โปรแกรมด้านล่างผลิตในโหมดการแก้ปัญหาในขณะที่ในโหมดการเปิดตัวการแสดงStart - Done - Finalize logfile Start - Finalize - Doneอย่างหลังคือสิ่งที่ฉันคาดหวัง

class Program
{
    private static string logfile = @"c:\temp\log.txt";
    public static void Main(string[] args)
    {
        File.WriteAllText(logfile, "Start\n");
        SomeClass some = new SomeClass("Hello World!");
        some = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        File.AppendAllText(logfile, "Done\n");
    }
}

class SomeClass 
{
    private static string logfile = @"c:\temp\log.txt";
    public string SomeString { get; set; }
    public SomeClass(string s) { SomeString = s; }
    ~SomeClass()
    {
        File.AppendAllText(logfile, "Finalize\n");
    }
}

คำตอบ

1 V0ldek Aug 16 2020 at 23:33

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

ฉันจะเปลี่ยนเส้นทางคุณไปยังโพสต์ดีๆจาก Eric Lippert เกี่ยวกับเรื่องนี้ซึ่งเรียกว่า"เมื่อทุกสิ่งที่คุณรู้ไม่ถูกต้อง" โดยเฉพาะอย่างยิ่ง:

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

และแม้กระทั่งกับGC.Collect():

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

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

ฉันเขียนคำอธิบายโดยละเอียดเพิ่มเติมเกี่ยวกับคำถามนี้เพื่อให้คุณสามารถใช้เป็นจุดเริ่มต้นในการค้นคว้าเพิ่มเติม