본문 바로가기
개발/Unity 내일배움캠프 TIL

C# 생성자와 소멸자 그리고 가비지 컬렉터

by 석시 2023. 8. 17.



생성자와 소멸자를 새로 알아본다는 느낌보다는 정확히 언제 호출되는지, 어떻게 작성해야하는지 등의 이야기를 위주로 해보겠다.

생성자 (Constructor)

class Person
{
    private string name;
    private int age;

    // 매개변수가 없는 디폴트 생성자
    public Person()
    {
        name = "Unknown";
        age = 0;
    }

    // 매개변수를 받는 생성자
    public Person(string newName, int newAge)
    {
        name = newName;
        age = newAge;
    }

    public void PrintInfo()
    {
        Console.WriteLine($"Name: {name}, Age: {age}");
    }
}

생성자가 생긴 것을 보면 반환값이 없고 이름이 클래스와 같은 함수처럼 생겼다.

생성자는 언제 호출되느냐?

바로 new를 호출할 때 메모리를 할당 받고 나서 실행된다.

아무 생성자를 만들지 않으면 기본적으로는 아무 매개변수도 받지 않는 생성자 하나가 기본으로 생성된다.

위 예제에서 아무 생성자를 만들지 않는다면 Person() 하나를 기본으로 넣어주는 것이다.

이를 생성자 오버로딩을 통해 초기화를 하거나 로그를 출력하는 등

객체 생성 시점에서 다양한 작업들을 해줄 수 있다.

하나 조심해야하는 것이 생성자 오버라이딩을 생성해주는 순간 기본으로 만들어주던 생성자 Person()은 없어진다.

없어진다기보다 더이상 기본으로 생성을 해주지 않는다고 표현해야 더 맞겠다.

따라서 매개변수를 받는 생성자를 만들어줄 때, Person() {};과 같이 매개변수가 없는 가장 기본의 생성자를 같이 선언해주도록 하자.

소멸자 (Destructor)

~Person()
{
    Console.WriteLine("Person 객체 소멸");
}

객체가 소멸되는 시점에 호출되는 메서드…라지만

메모리 해제에 심혈을 기울여야 하는 C & C++과는 달리 C#.NET에서 제공해주는 자동 메모리 관리 기능이 매우 강력하기 때문에 사실 명시적으로 호출할 일은 잘 없다.

오버라이딩은 따로 안된다.

있을 이유가 없기도 하고.

다만, 이러한 메모리 해제가 언제 이루어지는지는 알아두면 좋다.

가비지 컬렉터 (GC)

GC가 언제 이루어지냐면 보통은 특정 세대가 차지하는 메모리의 공간이 기준점을 넘어갈 때 GC를 해주게 된다.

객체 할당을 위한 메모리는 힙에서 할당이 되는데, 이 힙이 스택이나 정적 필드 등에게서 참조가 되고 있는 상태가 아니라면 GC는 이를 가비지라고 판단한다.

갑자기 세대라는 말이 나와서 당황스러운데, 0, 1, 2세대밖에 없다.

GC의 세대라는 개념은 GC를 효율적으로 가비지일 가능성이 높은 애들만 쏙쏙 확인을 해주기 위해 도입한 개념이다.

객체가 새로 생성되면 0세대, GC를 한 번 했을 때 살아남았다면 (Garbage가 아니라면) 1세대, GC를 두 번 했을 때 살아남았다면 2세대이다.

보통 0세대 > 1세대 > 2세대 순으로 GC를 자주 하게 된다.

0세대 GC를 해도 메모리에 자리가 없으면 1세대 GC를 수행하고, 그래도 부족하면 2세대 GC를 수행하는 방식이다.

2세대까지 GC를 하면 GC의 수행범위가 너무 넓어 성능의 하락을 야기할 수 있으므로,

빈번한 GC 수행의 방지를 위해 다음과 같은 항목들이 권장된다.

  1. 대형 객체 할당을 자제한다.

    너무 큰 객체를 할당하면 LOH(Large Object Heap)이라고 해서 바로 2세대로 가버린다. 따라서 해당 대형 객체 할당은 자제하는 것이 좋다.

  1. 객체 복사가 일어나는 항목들을 자제하자.

    이게 좀 골때린다.

    예를 좀 보면 알 수 있을텐데, string에서의 +는 append처럼 보이지만, 사실 String 객체를 하나 더 생성해서 그곳에 값을 전부 복사해주는 방식으로 수행된다!

    따라서 연산을 수행하고 남은 기존의 string은 가비지로 전락해버릴 여지가 있기에, 이런 것들을 조심하자는 것이다.

    객체 복사가 일어나는 항목들을 열거해보자.

    • string+ 연산
    • stringToUpper() & ToLower()
    • 박싱 (값 형식 → 참조 형식)
    • 언박싱 (참조 형식 → 값 형식)
    • ListAdd() & Remove()

  1. 외부로부터 리소스를 받아올 때 using 사용

    using을 사용해 리소스를 받아오면, 코드블럭 {}이 끝날 때 자동으로 메모리에서 해제를 해준다.

    GC가 필요 없는 것이다.


Uploaded by N2T