พฤติกรรมการเก็บขยะสำหรับนักทำลาย


9

ฉันมีคลาสง่าย ๆ ที่กำหนดไว้ด้านล่าง

public class Person
{
    public Person()
    {

    }

    public override string ToString()
    {
        return "I Still Exist!";
    }

    ~Person()
    {
        p = this;

    }
    public static Person p;
}

ในวิธีการหลัก

    public static void Main(string[] args)
    {
        var x = new Person();
        x = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine(Person.p == null);

    }

ตัวรวบรวมขยะควรอ้างอิงหลักสำหรับ Person.p หรือไม่และจะเรียกตัวทำลายเมื่อใด


แม่: destructor แบบใน C # เป็น ment จะเป็นfinalizer ประการที่สอง: การตั้งค่าเดี่ยวอินสแตนซ์ของอินสแตนซ์ที่มีการสรุปดูเหมือนว่าความคิดที่ดีมาก ที่สาม: สิ่งที่เป็นPerson1? Personผมเห็นเท่านั้น สุดท้าย: ดูdocs.microsoft.com/dotnet/csharp/programming-guide/…สำหรับการทำงานของ finalizers
HimBromBeere

@HimBromBeere Person1เป็นจริงPersonแก้ไขการพิมพ์ผิด
Parimal Raj

@HimBromBeere นี่เป็นคำถามสัมภาษณ์จริงตอนนี้ตามความเข้าใจของฉันแล้ว CG.Collect ควรจะเรียกใช้ destructor แต่ก็ไม่ได้
Parimal Raj

2
(1) ถ้าคุณอ้างอิงวัตถุที่ถูกสรุปภายในเป็นไฟนอลไลเซอร์มันก็จะไม่ถูกเก็บขยะจนกว่าการอ้างอิงนั้นจะไม่สามารถเข้าถึงได้จากรูทอีกต่อไป (2) จุดในเวลาที่เรียกว่า finalizer ไม่สามารถคาดเดาได้
Matthew Watson

@HimBromBeere และเมื่อฉันใส่เบรกพอยต์ที่ Console.WriteLine Person.p ขึ้นมาเป็นโมฆะโดยไม่คำนึงถึงการGC.Collectโทร
Parimal Raj

คำตอบ:


13

สิ่งที่คุณขาดหายไปคือคอมไพเลอร์ยืดอายุการใช้งานxตัวแปรของคุณไปจนถึงจุดสิ้นสุดของวิธีการที่กำหนดไว้ - นั่นเป็นเพียงบางสิ่งที่คอมไพเลอร์ทำ - แต่มันทำเพื่อ DEBUG build เท่านั้น

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

ผลลัพธ์ของรหัสต่อไปนี้คือ:

False
True

และรหัส:

using System;

namespace ConsoleApp1
{
    class Finalizable
    {
        ~Finalizable()
        {
            _extendMyLifetime = this;
        }

        public static bool LifetimeExtended => _extendMyLifetime != null;

        static Finalizable _extendMyLifetime;
    }

    class Program
    {
        public static void Main()
        {
            test();

            Console.WriteLine(Finalizable.LifetimeExtended); // False.

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

            Console.WriteLine(Finalizable.LifetimeExtended); // True.
        }

        static void test()
        {
            new Finalizable();
        }
    }
}

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

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

โค้ดต้นฉบับทำงานได้ตามที่คาดไว้สำหรับบิลด์รีลีส - ดังนั้นโค้ดต่อไปนี้จะออกมาfalse, trueสำหรับบิลด์ RELEASE และfalse, falseสำหรับบิลด์ DEBUG:

using System;

namespace ConsoleApp1
{
    class Finalizable
    {
        ~Finalizable()
        {
            _extendMyLifetime = this;
        }

        public static bool LifetimeExtended => _extendMyLifetime != null;

        static Finalizable _extendMyLifetime;
    }

    class Program
    {
        public static void Main()
        {
            new Finalizable();

            Console.WriteLine(Finalizable.LifetimeExtended); // False.

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

            Console.WriteLine(Finalizable.LifetimeExtended); // True iff RELEASE build.
        }
    }
}

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

ในคำอื่น ๆ คุณสามารถให้วัตถุ "อยู่ในการดำเนินการ" ผ่าน finalizer นี่ถือเป็นการออกแบบที่ไม่ดีเลยทีเดียว!

ตัวอย่างเช่นในรหัสด้านบนที่เราทำ_extendMyLifetime = thisใน finalizer เรากำลังสร้างการอ้างอิงใหม่ไปยังวัตถุดังนั้นตอนนี้มันจะไม่ถูกเก็บรวบรวมขยะจนกว่า_extendMyLifetime(และการอ้างอิงอื่น ๆ ) จะไม่มีการอ้างอิงอีกต่อไป

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