[C++] Garbage Collection & Reference Counting
프로그래밍 언어엔 메모리를 프로그래머가 직접 관리해야하는 unmanaged 언어와 언어단에서 관리해주는 Managed 언어가 있다.
대표적인 예로 C/C++가 Unmanaged 언어이며 C#, JAVA가 Managed 언어이다
대부분의 Managed 언어에서는 메모리 관리를 하기 위해 다음중 하나의 기법을 사용한다.
- Garbage collection
- Reference counting
🔖Garbage Collection
힙 메모리에 주소가 할당하여 사용중에 메모리 주소를 새로 할당하여 가리키는 주소값이 변경되거나 형변환이 되면서 주소를 잃어버리게 되고, 다시 찾을 수 없게 되는 메모리가 생겨버리게 되는데 이렇게 발생하는 가비지 메모리를 가비지 컬렉터(Garbage collector)가 추적하고 해제하게 된다.
가비지 컬렉션은 프로그램 실행 중에 일정 주기마다 또는 특정 조건일 경우(보통 힙 메모리가 충분치 않을 경우)에 스택과 같이
변수가 저장되는 곳(root)를 훑으면서 사용되지 않는 메모리를 추적하여 삭제한다.
사용되지 않는 메모리의 기준은 root로부터 어떻게든 접근될 수 있냐 이다.
Garbage Collection의 한계
- 어떤 방식의 가비지 컬렉션을 사용하든 실행 시간에 작업을 하는 이상 성능 하락을 피할 수는 없다 (특정 주기 또는 조건에따라 한번에 가비지 컬렉팅을 수행하기 때문에)
-
가비지 컬렉터가 존재하더라도 더 이상 접근이 불가능한 객체만 회수하기 때문에 메모리 누수는 발생할 수 있다
🔖Reference Counting
Reference counting의 동작 방식은 간단하다. 동적으로 할당된 메모리 주소가 참조될 때마다 count를 1씩 증가시키고, 참조를 끊을 땐 1 감소 시킴으로써 count를 체크하여 0이 될 경우 더 이상 참조되지 않는 메모리로 판단하고 즉시 메모리를 해제하는 방식이다.
대표적으로 사용하는 곳
- Component Object Model(COM)
- 마이크로소프트의 Iunknown, ATL
- Perl, Python, Squirrel
- C++ shared_ptr
- Glib(Gobject), Apache APR
C/C++은 직접 메모리를 관리 해주어야 하기 때문에 레퍼런스 카운트를 아래와 같이 간단한 클래스로 구현하여 메모리 참조와 해제를 자동화 시킬 수 있다.
template<class _Ty>
class CAutoReference
{
public:
// Default Constructor
CAutoReference()
:ptr_(nullptr), ref_count_(nullptr)
{
}
// Constructor Direct Allocation
CAutoReference(size_t alloc_counts)
{
ptr_ = CEsayMemory::Alloc<_Ty>(alloc_counts);
ref_count_ = CEsayMemory::Alloc<size_t>();
*ref_count_ = 0;
++(*ref_count_);
}
// Constructor Move Allocation
CAutoReference(_Ty* ptr)
:ptr_(ptr), ref_count_(nullptr)
{
if(nullptr == ref_count_)
{
ref_count_ = CEsayMemory::Alloc<size_t>();
*ref_count_ = 0;
}
++(*ref_count_);
}
// Copy Constructor
CAutoReference(const CAutoReference& other)
:ptr_(other.ptr_), ref_count_(other.ref_count_)
{
++(*ref_count_);
}
// Destructor
~CAutoReference()
{
if (ref_count_)
{
if (0 == (--(*ref_count_)))
{
CEsayMemory::Free(ptr_);
CEsayMemory::Free(ref_count_);
}
}
}
// Operator Overloading ==============================================
CAutoReference& operator=(const CAutoReference& rhs)
{
if(this == &rhs)
{
return *this;
}
if(ptr_ == rhs.ptr_)
{
return *this;
}
this->~CAutoReference();
++(*rhs.ref_count_);
ptr_ = rhs.ptr_;
ref_count_ = rhs.ref_count_;
return *this;
}
_Ty& operator*() const
{
return *ptr_;
}
_Ty* operator->() const
{
return ptr_;
}
_Ty& operator[](size_t index) const
{
return ptr_[index];
}
// ===================================================================
private:
_Ty* ptr_;
size_t* ref_count_;
};