placement new와 메모리풀

JellyPower·2023년 5월 4일
0

나만 몰랐던 C++

목록 보기
7/11
post-thumbnail

placement new(위치지정 new)

  • 내가 원하는 메모리에 직접 객체를 할당하는 것을 placement new라고 한다.
  • 다른 관점에서 본다면 생성자를 명시적으로 호출해주는 기능이라고도 볼 수 있다.
  • 문법은 꽤 간단한 편이다: new (MemAddr) ClassType(parameter);

예시

#include<cstdio>
#include<memory>

class ExamClass {

public:
	ExamClass(int idIN) : id(idIN) {}
	~ExamClass() { printf("class ID.%d deleted!\n", id); }

	int id;

	virtual void PrintClassID() const {
		printf("class ID: %d\n", id);
	}
};

int main() {
	char* MemoryPool = new char[sizeof(ExamClass)*10];
	ExamClass* MemPoolBaseAddr = (ExamClass*)MemoryPool;

	for (int i = 0; i < 10; i++) {
		new (MemPoolBaseAddr + i) ExamClass(i*10+1);
		// 메모리 할당이 직접 일어나는 것이 아닌 내가 원하는 위치에 바로 객체값만 생성 가능
	}
	
	for (int i = 0; i < 10; i++) {
		(MemPoolBaseAddr+i)->PrintClassID();
		// 함수 실행도 잘 된다.
	}

	for (int i = 0; i < 10; i++) {
		(MemPoolBaseAddr + i)->~ExamClass();
		// 해제할때는 delete가 아닌 명시적으로 소멸자를 호출해줘야 한다.
	}

	delete[] MemoryPool;
	return 0;
}

C++에서 placement new를 지원하는 이유

1. 생성자를 명시적으로 호출하고 싶기 때문에

  • C++에선 원래 기존 문법으로 소멸자는 명시적으로 호출 할 수 있게 해주더라도 생성자를 명시적 호출하는 것을 막았었다. ex) instance.ExamClass(); //=> 컴파일 에러
  • 그런데 생성자를 명시적으로 호출하면 이득을 얻을 수 있는 상황이 많이 있었기에 C++는 생성자 호출의 명시적 호출을 지원하기 위해 placement new를 도입했다.

2. 이미 할당된 메모리에 어떠한 객체를 생성하고 싶기 때문에

동적할당/해제는 오래걸립니다

  • 기본적으로 대부분의 언어에서 특정 데이터를 동적으로 생성하고 삭제하는 작업은 비용이 많이 드는 작업이다.
  • C++에선 GC도 없는데 왜 메모리 해제가 오래 걸리냐고 생각 할 수 있겠지만, 힙메모리의 경우 객체의 생성과 소멸 시점이 예측불가능 하기 때문에 가용 가능한 연속된 메모리 공간이 이빨빠진듯이 흩어지는 heap fragmentation이라는 현상이 발생한다.
    (운영체제의 external fragmentation과 유사함)
  • C/C++컴파일러들은 이러한 이빨 빠진 영역들을 최대한 활용케 하기 위해 중간중간 이빨빠진 메모리 영역들을 기록하고 다시 합쳐주는 작업을 거친다. 그래서 동적메모리의 할당과 해제에는 오랜 시간이 걸린다. (C#과 같은 GC가 있는 언어에서도 이러한 문제는 생기기에 GC가 알아서 메모리를 Compaction해준다)

메모리풀을 쓰는 이유

  • 그렇기 때문에 많은 C++프로그래머들은 메모리풀을 활용한다.
  • 내가 사용하고자 하는 메모리 영역을 미리 할당한 이후 이미 할당된 영역에 내가 사용하고자 하는 객체들을 생성하고 해제하면 실질적인 동적할당된 객체에서 일어나는 할당 가능한 메모리를 찾는 시간메모리를 해제하고 병합하는 시간을 줄일 수 있다.

더욱 자세한 내용을 원한다면?

아래 링크를 참조해주세요!

성능을 위한 메모리 사용 tip

profile
게임엔진코드싸개(진)

0개의 댓글