Krafton Jungle/2. Keywords

[WEEK07] 가비지 컬렉션과 메모리 관련 오류들

munsik22 2025. 4. 28. 23:30

Garbage Collection

  • Garbage Collection(이하 GC)는 heap에서 할당된 저장공간의 자동화된 회수이다: 응용프로그램은 free를 해 줄 필요가 없다
  • 파이썬, 루비, 자바 등 많은 동적 언어에서 사용한다.
  • C나 C++에도 보수적인Conservative GC 비슷한 무언가가 있지만 모든 garbage를 collect하지는 못한다.
  • 묵시적 메모리 관리자는 어떻게 메모리가 free될 수 있는지 알 수 있을까?
    • 일반적으로 우리는 미래에 어떻게 될지 알지 못한다.
    • 하지만 우리는 특정한 블록이 연결된 포인터가 없다면 사용되지 않을 것이라는 것을 알 수 있다.
  • 포인터에 대해 특정한 추측을 해야 한다.
    • 메모리 관리자는 포인터와 non-pointer를 구분할 수 있다.
    • 모든 포인터들은 블록의 시작을 가리킨다.
    • 포인터를 숨길 수 없다.

메모리를 그래프로 표현하기

  • 우리는 메모리를 directed graph로 볼 수 있다.
    • 각 블록은 그래프의 노드가 된다.
    • 각 포인터는 그래프의 에지가 된다.
    • 힙으로의 포인터를 포함하는 힙 외부의 공간들을 root 노드라고 한다. (예: 레지스터, 스택 상의 공간, 전역변수 등)
    • 노드(블록)이 어떤 노드에 연결되어 있다면 reachable하다.
    • Non-reachable 노드들은 garbage다.

Mark and Sweep Collecting

  • malloc/free 패키지 위에서 빌드할 수 있다: space 밖으로 나갈 때까지 malloc을 사용해서 할당한다.
  • space 밖으로 나갈 때:
    • 각 블록의 헤드에 추가적인 mark bit를 사용한다.
    • Mark : root에서 시작해 각 reachable 블록에 mark bit을 set한다.
    • Sweep : 모든 블록을 스캔해서 mark되지 않은 블록들을 free한다.

단순한 구현을 위한 가정

  • 응용프로그램
    • new(n) : 모든 장소가 clear된 상태에서 새로운 블록을 가리키는 포인터를 return한다.
    • read(b, i) : 레지스터 내의 블록 b의 주소 i를 읽는다.
    • write(b, i, v) : 블록 b의 주소 i에 v를 쓴다.
  • 각 블록은 헤더 워드를 가진다.
    • 블록 b에 대해서 b[-1]의 주소를 가진다.
    • 각 collector에서 각각 다른 목적으로 사용한다.
  • GC에서 사용되는 인스트럭션
    • is_ptr(p) : p가 포인터인지 결정한다.
    • length(b) : 헤더를 제외한 블록 b의 길이를 return한다.
    • get_roots() : 모든 root들을 return한다.
  • 메모리 그래프에서 DFS를 사용해 mark하기
ptr mark(ptr p) {
   if (!is_ptr(p)) return;        // do nothing if not pointer
   if (markBitSet(p)) return;     // check if already marked
   setMarkBit(p);                 // set the mark bit
   for (i=0; i < length(p); i++)  // call mark on all words
     mark(p[i]); 		    //   in the block
   return;
}
  • 다음 블록을 찾기 위해 length를 사용해 sweep하기
ptr sweep(ptr p, ptr end) {
   while (p < end) {
      if markBitSet(p)
      	clearMarkBit();
      else if (allocateBitSet(p)) 
         free(p);
      p += length(p);
}

C에서의 보수적인 Mark & Sweep

  • C 프로그램을 위한 "보수적인conservative GC"
    • is_ptr()은 한 워드가 포인터인지를 그것이 메모리에서 한 할당된 블록을 가리키는지 확인함으로써 결정한다.
    • 하지만 C 포인터는 블록의 중간을 가리킬 수도 있다.
  • 그래서 어떻게 블록의 시작을 찾을래?
    • 모든 할당된 블록들을 추적하기 위해 균형 이진 트리를 사용할 수 있다. (키는 블록의 시작)
    • 균형 트리 포인터는 헤더에 저장될 수 있다. (2개의 추가적인 워드를 사용해야 함)


Memory-related perils and pitfalls

  • 나쁜 포인터 참조 해제
  • 초기화되지 않은 메모리 읽기
  • 메모리 덮어쓰기
  • 존재하지 않는 변수 참조
  • 블록을 여러 번 해제하기
  • 자유 블록 참조
  • 블록을 해제하지 못함

메모리 버그들을 다루는 방법

  • 디버거 (gdb)
  • 일관성 체커 자료구조 사용
  • 이진 번역기 (valgrind)
  • glibc malloc은 확인 코드를 포함한다: setenv MALLOC_CHECK_3

'Krafton Jungle > 2. Keywords' 카테고리의 다른 글

[ALWAYS] 정글 공부 키워드  (0) 2025.04.30
[WEEK07] DMA와 이더넷  (0) 2025.04.29
[WEEK07] System Call  (0) 2025.04.28
[WEEK07] Demand-zero Memory  (0) 2025.04.28
[WEEK07] 가상 메모리와 페이징  (0) 2025.04.28