자동 메모리 관리
std::shared_ptr를 배우기 전에, 자동 메모리 관리에 관해 알아야 합니다. 주로 쓰는 두가지 기법이 있었습니다. 첫번째는 가비지 컬렉션이고, 두번째는 참조 카운팅이 있습니다. 가비지컬렉션은 Java와 C#에서 활용되고 있으며, 참조 카운팅은 Swift와 오브젝트 C에서 지원됩니다.
가비지 컬렉션
보통 트레이싱 가비지 컬렉션을 의미합니다. 메모리 누수를 막으려는 시도에서 나오게 되었습니다. 주기적으로 컬렉션을 실행합니다. 충분한 여유 메모리가 없을 때 컬렉션이 실행됩니다. 스케쥴에 따라 또는 수동으로도 실행 가능합니다. 매 주기마다 GC는 루트를 확인합니다. 즉, 전역 변수, 스택, 레지스터들을 확인합니다. 힙에 있는 개체에 루트를 통해 접근할 수 있는 지 판단합니다. 접근할 수 없다면 가비지로 간주해서 해제합니다.
가비지 컬렉션의 문제점
사용되지 않는 메모리를 즉시 정리하지 않습니다. 가비지 컬렉션이 메모리를 해제해야 하는 지 판단하는 동안 애플리케이션이 멈추거나 버벅일 수 있습니다.
참조 카운팅
가비지 컬렉션처럼 개체에 대한 참조가 없을 때 개체가 해제됩니다. 언제든 참조 횟수를 활용해서 특정 개체가 몇 번이나 참조되고 있는 지 판단 가능합니다. 어떤 개체 a를 다른 개체 b가 참조할 때 횟수가 늘어납니다. B가 참조를 그만 둘 때 횟수가 줄어듭니다. COM이 수동 참조 카운팅을 지원해주며, std::shared_ptr는 이걸 자동으로 구현하여 줍니다.
강한 참조
강한 참조란 개체 A가 개체 B를 참조할 떄, 개체 B는 절대 소멸되지 않음을 의미합니다. 강한 참조의 수를 저장하기 위해 강한 참조 카운트를 사용합니다. 일반적으로 새 인스턴스, 즉 개체에 대한 참조를 만들 때 강한 참조 횟수가 늘어납니다. 강한 참조 횟수가 0이 될 때, 해당 개체는 소멸됩니다.
참조 카운팅의 문제점
참조 횟누는 너무 자주 바뀝니다. 멀티 쓰레드 환경에서 안전하려면 lock이나 atomic 연산이 필요합니다. ++mRefCount보다 확연히 느립니다.
또 순환 참조의 문제가 있습니다. 개체 A가 개체 B를 참조하고 반대로 개체 B가 개체 A를 참조합니다. 이러한 경우 절대 해제되지 않습니다. 이러한 경우에는 C++에는 해결책이 있습니다.
가비지 컬랙션, 참조카운트를 쓰면 메모리 누수가 없나?
전통적인 메모리 누수는 없습니다. delete를 까먹은 경우 보통 이런 실수를 많이 나지만, 발견하면 고치기는 쉽습니다.
하지만 가비지 컬랙션, 참조카운트를 쓰는 경우 여전히 메모리 누수가 발생할 수 있습니다. 이런 실수는 덜하지만, 발견한다고 해도 고치기 쉽지 않습니다. 이런 방식으로 사고하는 훈련이 되어 있지 않으면 자신이 발생시킨 메모리 누수를 스스로 바로잡지 못할 것입니다.
가비지 컬렉션 vs 참조 카운팅
가비지 컬렉션은 사용하기 훨씬 쉽습니다. 실시간 또는 고성능 프로그램에 적합하지 않습니다. 고성능 게임이나 영상에는 좋지 않습니다.
참조 카운팅 역시 사용하기는 어렵지 않습니다. 실시간 또는 고성능 프로그램에 적합합니다. 멀티 스레드 환경에서는 순수한 포인터 보다 훨씬 느립니다.
'SW > C++' 카테고리의 다른 글
C++ : weak_ptr : 약한 참조 개념, 사용법, 예제, 구현 (0) | 2020.05.09 |
---|---|
C++ : shared_ptr : 개념, 예제, 사용법, 구현 (0) | 2020.05.08 |
C++ : unique_ptr : 배스트 프렉티스, 기본 예제, 활용 방법, 동작 방식 (0) | 2020.05.06 |
C++ : unique_ptr : reset, get, release, move, 복사 : 개념, 예제 (0) | 2020.05.05 |
C++ : unique_ptr : 유니크 포인터 개념, 필요성, 장점, 활용법 (0) | 2020.05.04 |