소멸자에 대한 가비지 콜렉터 동작


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의 기본 참조로, 언제 소멸자가 호출됩니까?


먼저 C #의 소멸자가 finalizer가 됩니다. 둘째 : 싱글 인스턴스를 최종 인스턴스로 설정하는 것은 매우 나쁜 생각 처럼 보입니다 . 셋째 : 무엇 Person1입니까? 나만 볼 수 Person있습니다. 마지막 : 파이널 라이저 작동 방식 은 docs.microsoft.com/dotnet/csharp/programming-guide/… 를 참조하십시오 .
HimBromBeere

@HimBromBeere Person1는 실제로 Person오타를 수정했습니다.
Parimal Raj

@HimBromBeere 이것은 실제로 인터뷰 질문이었습니다. 이제 CG.Collect는 소멸자를 호출해야했지만 실제로는 그렇지 않았습니다.
Parimal Raj

2
(1) 파이널 라이저 내에서 마무리되는 객체를 다시 참조하면 루트에서 해당 참조에 더 이상 도달 할 수 없을 때까지 IT 가비지 수집되지 않습니다 (따라서 가비지 수집이 지연되는 효과가 있습니다). (2) 파이널 라이저가 호출되는 시점은 예측할 수 없습니다.
Matthew Watson

@HimBromBeere 그리고 Console.WriteLine Person.p에 중단 점을 넣을 때 GC.Collect전화에 관계없이 null로 나타납니다
Parimal Raj

답변:


13

여기서 누락 된 것은 컴파일러가 x정의 된 메소드의 끝까지 컴파일러의 변수 수명을 연장 한다는 것입니다. 즉 컴파일러가하는 일 이지만 DEBUG 빌드를 위해서만 수행합니다.

변수가 별도의 방법으로 정의되도록 코드를 변경하면 예상대로 작동합니다.

다음 코드의 출력은 다음과 같습니다.

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()- 당신이 명시 적으로 널 (null)로 설정 한 경우에도!

위에서 언급했듯이 이것은 DEBUG 빌드에서만 발생합니다. 아마도 메소드의 끝으로 디버깅하는 동안 로컬 변수의 값을 검사 할 수 있습니다 (그러나 추측 일뿐입니다!).

원래 코드는 릴리스 빌드에서 예상대로 작동하므로 false, trueRELEASE 빌드 및 false, falseDEBUG 빌드에 대해 다음 코드 출력 이 있습니다 .

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에서 무언가를 수행하면 해당 객체가 더 이상 존재하지 않을 때까지 해당 객체는 가비지 수집되지 않습니다. 참조.

즉, 종료자를 통해 개체에 "실행 상태"를 제공 할 수 있습니다. 그러나 이것은 일반적으로 나쁜 디자인으로 유지됩니다!

예를 들어, 위 코드 _extendMyLifetime = this에서 finalizer에서 수행 하는 객체에 대한 새 참조를 작성하므로 이제는 _extendMyLifetime더 이상 참조하지 않을 때까지 가비지 수집 되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.