Skip to content

Latest commit

 

History

History
74 lines (61 loc) · 4.9 KB

Item50.md

File metadata and controls

74 lines (61 loc) · 4.9 KB

항목 50. new 및 delete를 언제 바꿔야 좋은 소리를 들을지를 파악해 두자

operator new와 operator delete를 바꾸게 되는 흔한 이유 3가지

잘못된 힙 사용을 탐지하기 위해

  • new한 메모리에 delete를 잊어버리는 경우
  • 한 번 new한 메모리에 두 번 delete를 하는 경우
  • 할당된 메모리 블록의 끝을 넘어 뒤에 기록 하는 오버런(할당된 메모리 블록의 끝을 넘어 뒤에 기록하는 것)이 발생하는 경우
  • 할당된 메모리 블록의 시작을 넘어 앞에 기록하는 언더런(할당된 메모리 블록의 시작을 넘어 앞에 기록하는 것)이 발생하는 경우

효율을 향상시키기 위해

  • 컴파일러가 제공하는 기본 버전의 operator newoperator delete 함수는 일반적인 쓰임새에 맞추어 설계된 것이다.
  • 메모리 관리자에 대한 요구사항은 다양하다.
  • 기본적으로 제공하는 new, delete 함수는 안정적으로 구현되어 있다.
  • 사용자 정의 newdelete를 사용하여 우수한 성능(빠른 속도 적은 메모리)을 낼 수 있다.
    • 개발자가 자신의 프로그램이 동적 메모리를 어떤 성향으로 사용하는지를 제대로 이해하고 있어야 한다.
    • 응용프로그램에 따라서는 newdelete를 사용자 정의 버전으로 바꾸는 것만으로 성능 향상을 이뤄낼 수 있다.

동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해

  • 할당된 메모리 블록 크기의 분포
  • 메모리가 할당되고 해제되는 순서 (FIFO? LIFO? random?)
  • 사용 패턴이 바뀌는지?
  • 동적 할당 메모리의 최대량은?

메모리 관리자를 만드는 일

static const int signature = 0xDEADBEEF;
typedef unsigned char Byte;

void* operator new(std::size_t size) throw(std::bad_alloc) {
	using namespace std;
     
     // 경계표지 2개를 앞뒤에 붙일 수 있을 만큼만 메모리 크기를 늘린다.
	size_t realSize = size + 2* sizeof(int);

     // malloc을 호출하여 실제 메모리를 얻어낸다.
	void* pMem = malloc(realSize);
     if(!pMem) throw bad_alloc();

     // 메모리 블록의 시작 및 끝부분에 경계표지를 기록
	*(static_cast<int*>(pMem)) = signature;
	*(reinterpret_cast<int*>(static_cast<Byte*>(pMem) + realSize + sizeof(int))) = signature;

     // 앞쪽 경계표지 바로 다음의 메모리를 가리키는 포인터를 반환
	return static_cast<Byte*>(pMem) + sizeof(int);
}
  • 개념적으로 보면 operator new를 직접 만드는 작업은 별로 어렵지 않다.
    • 바이트 정렬 같은 까다로문 문제가 숨어있다.
    • 모든 operator new 함수는 어떤 데이터 타입에도 바이트 정렬을 만족하는 포인터를 반환해야 한다.
  • 현실적으로 잘 돌아가는 메모리 관리자를 만들기는 어렵다.
    • 컴파일러에서도 디버깅 및 로깅을 지원한다.

new 및 delete의 기본제공 버전을 다른 것으로 대체하는 작업을 해야할 때

잘못된 힙 사용을 탐지하기 위해

동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해

할당 및 해제 속도를 높이기 위해

  • 기본으로 제공되는 범용 할당자는 사용자 정의 버전 보다 느린 경우가 많다.
    • 사용자 정의 버전이 특정 타입의 객체에 맞추어 설계되어 있으면 더욱 그렇다.

기본 메모리 관리자의 공간 오버헤드를 줄이기 위해

  • 범용 메모리 관리자는 사용자 정의 관리자 버전과 비교해서 속도가 느리고 메모리를 많이 잡아먹는 경우가 많다.
  • 크기가 작은 객체에 대해 튜닝된 할당자를 사용하면 오버헤드를 제거할 수 있다.

기본 할당자의 바이트 정렬 동작을 보장하기 위해

  • x86 아키텍처에서는 double이 8바이트 단위로 정렬되어 있을때 읽기 쓰기 속도가 빠르다.
  • 컴파일러 중 기본적으로 제공되는 operator new 함수가 double에 대한 동적 할당 시에 8바이트 정렬을 보장하지 않는 것이 있다.
  • 기본제공 operator new 대신 8바이트 정렬을 보장하는 사용자 정의 버전으로 바꿀 수 있다.

임의의 관계를 맺고 있는 객체들을 한곳에 나란히 모아 놓기 위해

  • 자료구조를 별도로 담을 별도의 힙을 생성해서 이들이 가능한 적은 페이지를 차지하도록 할 수 있다.
  • 메모리 군집화는 위치 지정 new 및 위치지정 delete를 통해 구현할 수 있다.

원하는 동작을 수행하도록 하기 위해

  • 응용프로그램 데이터의 보안 강화를 위해 해제한 메모리 블록에 0을 덮기 위해 사용자 정의 버전을 구현할 수 있다.
  • 공유 메모리를 조작하는 C APIC++옷을 입히는 위해 사용자 정의 버전을 구현할 수 있다.