Memory Allocator

new/delete와 사용하지 않는 이유?

  • 기능의 과도한 범용성
  • 메모리 할당 너머의 hidden behavior 수행
  • 불충분한 기능

Memory Managerment의 필요성

  • 메모리 사용 및 관리에 용이
    • 통계 지표를 산출 가능
  • 디버깅과 에러 체킹에 확장성 부여
  • 자체 어플리케이션에 맞는 메모리 관리

Memory Manager란?

post_thumbnail

  • 메모리를 하나의 청크로 선 할당
    • 이를 다시 여러 블록으로 나눔
    • freelist - 나누어진 블럭으로 이루어진 링크리스트
  • 할당 시, 사용자는 하나 이상의 메모리에 엑세스를 얻음
    • 메모리 블럭에 대한 포인터
    • 이 블럭은 사용 중으로 마킹
  • 반환 시, 유저가 메모리 매니저에게 직접 반환
    • 이 블럭은 가용 가능한 것으로 다시 마킹

Custom Memory Allocator의 요소

  1. 사용 난이드
    • 포인터보다는 핸들
    • 프론트 엔드 유저들에게도 용이한 복잡도
    • Garbage Collection과 Memory Coalescing
    • Garbage Collection: 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제
    • Memory Coalescing: 단편화로 인해 분산된 메모리공간들을 인접해 있는 것끼리 통합시켜 큰 메모리 공간으로 합치는 기법
  2. 퍼포먼스
    • 속도와 일관성
    • 메모리 할당/반환의 규칙화
    • 주소 참조의 지역성
  3. Overhead
    • 첫 할당 시 거대한 메모리 오버헤드 발생
    • 메모리 블럭 당 사이즈
    • 디버깅을 위한 메모리가 추가적으로 필요
  4. 디버깅
    • Memory Leak 체크
    • Memory Corruption 체크
    • Block들을 특정 값으로 초기화
    • 메모리 사용 패턴과 수치화 가능
    • 스파이크 지점을 탐색
    • 한 메모리 블럭의 평균 라이프 사이클

자동 메모리 관리

  • 일반적으로 Garbage Collector라 불림
  • 이 경우 메모리는 프로그래머에 의해 반환되지 않음
  • GB가 접근 불가능한 메모리를 스캐닝
  • 궁극적인 목표는 사용자가 메모리 관리로부터 해방하는 것
  • 프로그램 메모리 단계까지 접근할 수 있어야 하므로 구현은 쉽지 않음

Fragmentation 메모리 파편화

post_thumbnail

  • 메모리 관리에 있어 가장 큰 문제
  • 파편화로 인해 가용 가능한 메모리 범위를 벗어나는 경우가 많음

Stack Allocator

post_thumbnail

  • 파편화를 완전히 막음
  • 마지막으로 할당된 메모리를 가장 먼저 free

Fixed Sized Blocks - Pools

  • 이 역시 파편화를 완전히 막음
  • 범용성에서 어긋남
  • 다양한 사이즈의 Pool을 이용할 수는 있음
  • 그러나 필요 이상 사이즈의 메모리가 할당되는 경우가 발생

First Fit

post_thumbnail

  • 시작 지점부터 출발해 가용 가능한 첫 block을 찾으면 그냥 할당

Next Fit

  • 마지막으로 사용한 block부터 탐색 시작
  • roving pointer를 사용해 pointer가 chunk의 끝에 이르면 처음으로 돌아옴

Best Fit

  • 가용 가능한 block 중 가장 작은 사이즈의 block을 사용
  • 실용적이지는 않다고 함

Segregated Fit

  • 여러 사이즈의 Fixed Sized Blocks, 즉 Pool을 가짐
  • 가용 가능한 최소 사이즈의 Pool을 선택

Binary Buddy System

post_thumbnail

  • 바이너리 트리 구조로 메모리 관리
  • Block의 사이즈는 트리 레벨과 연관
  • 같은 크기의 다른 block을 buddy
  • 큰 Block이 필요한 경우, buddy와 merge되어 사용

Word

  • 하드웨어 또는 프로세서에 의해 제어되는, 고정 크기의 데이터 처리 안뒤
    • 컴퓨터가 한번에 처리할 수 있는 명령 크기
  • 꼭 byte일 필요는 없다
  • 하드웨어는 한번에 하나의 word에 대한 작업만 수행
  • 한 Word 보다 더 큰 메모리 접근이 필요한 경우?
    • 하나 이상의 operation을 통해 수행
// 예시
struct Student1 
{ 
  int Age;      // 4 bytes 
  char Initial_1; // 1 byte 
  char Initial_2; // 1 byte 
}; 

Student1 * pS = new Student1[2];

post_thumbnail

Instruction Number of Operation
pS[0].Age 1
pS[0].I1 1
pS[1].Age 2
  • 위 케이스에서 pS[1].Age는 두번의 작업이 수행되어야만 접근 가능
// 다른 예시
struct Student1 
{ 
  char Initial_1; // 1 byte 
  int Age;      // 4 bytes 
  char Initial_2; // 1 byte 
}; 

post_thumbnail

Instruction Number of Operation
pS[0].Age 2
pS[0].I1 1
pS[0].I2 1
pS[1].Age 2

Alignment

  • 데이터를 메모리 주소에 맞게 조정하는 것
  • Padding
    • 사용하지 않는 byte를 추가적으로 할당하는 것
struct Student1 
{ 
  int Age;      // 4 bytes 
  char Initial_1; // 1 byte 
  char Initial_2; // 1 byte 
};

// size of Student1 = 8
cout << "size of Student1 = " << sizeof(Student1) << endl; 

post_thumbnail

struct Student2 
{ 
  char Initial_1; // 1 byte 
  int Age;      // 4 bytes 
  char Initial_2; // 1 byte 
};

// size of Student1 = 12
cout << "size of Student2 = " << sizeof(Student2) << endl; 

post_thumbnail

출처

Categories: ,

Updated: