Publish:

태그: , , , , ,

카테고리:


🤯 언리얼을 하기 위해 C++ 기억 되살리기 프로젝트

  • 자동 메모리 관리
    • std::shared_ptr 을 배우기 전에, 자동 메모리 관리에 대해 알아보자~
    • 주로 쓰는 두 가지 기법
      • 가비지 컬렉션 (GC, Garbage Collection) : java와 C#에서 지원
      • 참조 카운팅 (RefCounting) : Swift와 애플 objective-C 에서 지원

가비지 컬렉션 (Garbage Collection)

  • 메모리 누수를 막으려는 시도
  • 주기적으로 컬렉션 실행
  • 충분한 여유 메모리가 없을 때, 컬렉션이 실행 됨
    • 스케쥴에 따라 수동으로도 실행 가능
  • 매 주기마다, GC는 루트(root)를 확인함
    • 전역 변수
    • 스택
    • 레지스터
  • 힙에 있는 개체에 루트를 통해 접근할 수 있는지 판단
  • 접근할 수 없다면, 가비지로 판단해 메모리 해제

작동원리 (단순화)

스택이 루트.
스택의 오브젝트를 하나씩 훑음. 이 오브젝트가 다른 오브젝트 멤버를 가지고 있는지 등 체크.
루트로 접근할 수 없는 개체 -> 아무도 참조하고 있지 않다는 것.
컬렉팅하여 삭제.

  • 모든 개체를 훑는데에 시간이 걸릴텐데 어떻게 최적화 되어 있는가? 혹은 어떻게 최적화 할텐가!

문제점

  • 사용되지 않는 메모리를 즉시 정리하지 않음
  • GC가 메모리를 해제해야 할지 판단하는 동안 프로그램이 멈추거나 버벅거릴 수 있음

참조 카운팅 (RefCounting)

가비지 컬렉션보다 실시간 프로그램에서 좀 더 나은 방법

  • 가비지 컬렉션처럼, 개체에 대한 참조가 없을 때 개체가 해제됨
    • 내 개체를 누가 안참조해, 안가지고 있어, 안 쓰고 있어 -> 바로 해제
  • 언제든 참조 횟수를 활용해서 특정 개체가 몇 번이나 참조되고 있는지 판단 가능
  • 어떤 개체 A를 다른 개체 B가 참조할 때 횟수가 늘어남
  • B가 참조를 그만둘 때 횟수가 줄어듦
    • B가 범위(Scope) 를 벗어나는 경우

작동 원리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class StringValue
{
    private:
        int mRefCount;              // 참조 카운트 확인용
                                    // (생성할 때 0으로 초기화 한다고 가정)
        char* mString;
};

class MyString
{
    private:
        StringValue* mStringValue;
};

MyString cat("Coco");               // 스택에 cat 오브젝트 생김
                                    // 오브젝트엔 mStringValue가 있고
                                    // 참조 카운트 1

MyString anotherCat("Lulu");        // 스택에 anotherCat 오브젝트 생김
                                    // 참조 카운트 1

anotherCat = cat;                   // anotherCat에 있던게 사라지고 cat이 들어가는 것
                                    // cat은 그럼 이제 2번 참조가 되는 오브젝트라는 것..
                                    // cat의 MyString 참조 카운트 2가 됨

강한(strong) 참조

  • 강한 참조란, 개체 A가 개체 B를 참조할 때, 개체 B는 절대 소멸되지 않음을 의미
  • 강한 참조의 수를 저장하기 위해 강한 참조 카운트를 사용
  • 일반적으로 새 인스턴스, 즉 개체에 대한 참조를 만들 때 강한 참조 횟수가 늘어남
  • 강한 참조 횟수가 0이 될 때 해당 개체는 소멸됨
    • shared_ptr 은 강함 참조, 약한 참조의 개념 둘 다 갖고 있음

문제점

  • 참조 횟수는 너무 자주 바뀜
    • 멀티 스레드 환경에서 안전하려면, lock 이나 원자적(atomic) 연산이 필요
    • ++mRefCount보다 확연히 느림
  • 순환(circular) 참조 발생하면…
    • 개체 A가 B를 참조
    • 개체 B가 A를 참조
    • 이럴 경우 절대 해제되지 않음! -> C++에 해결책이 있음

GC나 RefCount를 쓰면 메모리 누수가 없을까?

  • 전통적인 메모리 누수는 없음
    • delete를 잊었다거나
  • 하지만 여전히 발생할 수 있음
    • 위에서 언급한 순환 참조…

이슈 및 공부한 것을 기록해두는 개인 블로그 입니다. 댓글, 피드백 환영합니다 🙂

Update:

댓글남기기