서버/메모리관리
커스텀 Allocator
season97
2025. 6. 5. 15:18
728x90
반응형
코드 개요
1. BaseAllocator
class BaseAllocator
{
public:
static void* Alloc(int32 size);
static void Release(void* ptr);
};
void * BaseAllocator::Alloc(int32 size)
{
return ::malloc(size);
}
void BaseAllocator::Release(void * ptr)
{
::free(ptr);
}
- 기본적인 메모리 할당 함수로, malloc과 free를 래핑
- 추후 커스텀 할당자 구현 시 이 부분만 바꾸면 전체 시스템 수정 없이 적용 가능.
2. UBI_New
template<typename Type, typename... Args>
Type* UBI_New(Args&&... args) //... Args 키워드가 사용처에서 뭐가 올지 모르는 인자를 범용적으로 다 받아줄 수 있게 해주는키워드
{
// Type* memory = static_cast<Type*>(BaseAllocator::Alloc(sizeof(Type)));
Type* memory = static_cast<Type*>(ubialloc(sizeof(Type)));
//placement new
new(memory)Type(std::forward<Args>(args)...);
//일단 메모리는 있으니 굳이 만들 필요 없고. 생성자를 호출해줘!
//이렇게 해야 일반new처럼 작동함(new는 생성자를 자동 호출해주기때문에->디스어셈블리 까보면 나옴)
return memory;
}
- new를 직접 구현한 것.
- malloc으로 메모리만 먼저 확보한 후,
placement new로 지정된 위치에 객체 생성자 호출
- 일단 메모리는 있으니 굳이 만들 필요 없고. 생성자를 호출해줘!
new(memory) Type(std::forward<Args>(args)...);
- 이게 placement new 문법
- std::forward는 perfect forwarding을 위해 사용된다
- 이걸 사용함으로서 rvalue/lvalue 구분이 보존됨
- 전체 흐름:
- sizeof(Type) 크기만큼 메모리 확보
- 확보한 메모리에 Type 객체 생성자 호출
- 완성된 객체 포인터 반환
UBI_Delete
template<typename Type>
void UBI_Delete(Type* obj)
{
obj->~Type(); //소멸자 호출은 간단하다. d
//BaseAllocator::Release(obj);
ubirelease(obj);
}
- 일반 delete는 자동으로 소멸자 + 해제
- 여긴 수동으로 소멸자 호출 → 메모리 반환 순서대로 처리
- 역시 이걸로 delete를 대체.
매크로
/*---------------
Memory
---------------*/
#ifdef _DEBUG
#define ubialloc(size) BaseAllocator::Alloc(size)
#define ubirelease(ptr) BaseAllocator::Release(ptr)
#else
#define ubialloc(size) BaseAllocator::Alloc(size)
#define ubirelease(ptr) BaseAllocator::Release(ptr)
#endif
편하게 사용 및 Debug모드에서 추후 로그찍기 위해 매크로로 구현 (현재는 릴리지와 기능차이 없음)
✔왜 이렇게 할까?
목적 | 설명 |
직접 메모리 제어 | 메모리 풀, 커스텀 트래킹 등을 구현하기 위해 |
생성과 해제를 분리 | placement new는 객체 생성만, malloc은 메모리만 |
퍼포먼스 최적화 가능성 | 커스텀 allocator를 사용하면 성능, 캐싱, alignment 개선 가능 |
디버그/릴리즈 분기 | 디버깅용 로그나 추적 삽입 용이 |
728x90
반응형