Reference Counting과 Mark and Sweep

이용환
5 min readSep 25, 2018

--

인터넷 상의 많은 글에서 자바의 메모리 관리에 대한 내용을 다룬다.

내가 Garbage Collection을 처음 접했던 글은 네이버 D2에 올라온 Java Garbage Collection 이다.

학부 시절 이론으로만 배웠던 Garbage Collection 알고리즘은 Reference Counting이었고, 현재 Java의 일부 Garbage Collection 알고리즘은 Mark and Sweep 알고리즘을 개선하여 사용하는 것으로 알고 있다.

이 기회에 두 알고리즘에 대해 공부하고 정리해보았다.

Reference Counting

이 내용은 위키피디아의 Reference Counting 문서를 참고하여 작성하였습니다.

Reference Counting은 객체, 메모리 블록, 디스크 공간 등을 참조하는 Reference, Pointer, Handle 의 갯수를 저장하는 기술이다.

26 Line에서 지역 변수 ‘obj1’에 새로운 Obj(1) 객체가 할당된다. 이 때 Obj(1)에 대한 Reference Count는 1이다.

27 Line에서 지역변수 ‘obj2’에 새로운 Obj(2) 객체가 할당된다. 이 때 Obj(2)에 대한 Reference Count는 1이다.

28 Line에서 func 함수가 호출되고, 22Line func 함수 내에서 지역 변수 ‘obj3’에 새로운 Obj(3) 객체가 할당된다. 이 때, Obj(3)에 대한 Reference Count는 1이다.

그런데 func 함수가 끝남과 동시에 지역변수 ‘obj3’는 Stack Memory에서 제거되고, 이와 동시에 Obj(3)에 대한 Reference Count는 0이 된다.

29 Line에서 ‘obj1’에 새로운 객체 Obj(4)를 할당한다. 이에 Obj(1)의 Reference Count는 0이 된다.

30 Line에서 ‘obj2’에 ‘obj1’을 할당하므로써 Obj(4)에 대한 Reference Count는 1이 더 증가하여 2가 되고, 이와 동시에 Obj(2)는 Reference Count가 0이 된다.

프로그램이 끝나는 시점에서 메모리에 선언되었던 Obj(1) ~ Obj(4) 중 Reference Count가 1 이상인 것은 Obj(4)이고 나머지는 모두 Reference Count가 0이 된다. Reference Count가 0인 객체는 Garbage Collection의 대상이 된다.

C나 C++에서의 예제는 https://blogs.msdn.microsoft.com/abhinaba/2009/01/27/back-to-basics-reference-counting-garbage-collection/ 페이지에 잘 설명되어 있다.

Circular Referencing in Reference Counting

2개 이상의 객체가 서로를 참조하는 경우가 있다. 이러한 경우를 순환 참조라고 부른다.

위 코드 31, 32 Line에서 지역 변수 ‘obj1’과 ‘obj2’에는 각각 Obj(1)과 Obj(2)가 할당되어 Obj(1), Obj(2)는 모두 Reference Count가 1이 된다.

33, 34 Line에서 Obj(1)은 Obj(2)를 내부 변수 other에 할당하고 Obj(2)도 Obj(1)을 내부 변수 other에 할당하므로써 Obj(1), Obj(2)는 모두 Reference Count가 2가 된다.

35, 36 Line에서 지역 변수 ‘obj1’, ‘obj2’에 새로운 객체 Obj(3)와 Obj(4)를 할당하는데, Obj(1)과 Obj(2)의 Reference Count는 2에서 1로 줄어든다.

사실상 코드 내에서 Obj(1)과 Obj(2)에 접근할 방법은 없지만 Reference Count가 1이기 때문에 Garbage Collection의 대상이 되지 않는다.

이렇게 객체가 서로를 참조하는 현상을 Circular Referencing 이라고 하고, 이러한 문제점을 해결하기 위해 Mark and Sweep이라는 기법이 만들어졌다.

Mark and Sweep

Mark and Sweep은 생각보다 간단한 알고리즘이다.

위의 Wikipedia에 링크를 걸어놓았는데, 간단히 설명하면 아래와 같다.

출처: Wikipedia(https://en.wikipedia.org/wiki/Tracing_garbage_collection)
  1. 각 객체를 구성하는 메모리에는 Garbage Collection에서 사용하기 위한 1bit의 flag값이 존재한다. 이 값은 Garbage Collection이 일어나는 동안에만 활성화될 수 있다.
  2. Mark Stage: Root Set에서 접근 가능한 객체들의 flag를 ‘in-use’로 마킹한다. Mark Stage가 끝났을 때 flag가 ‘in-use’로 마킹되어 있는 부분은 모두 Root Set에서 접근 가능한 객체들이다.
  3. Sweep Stage: 모든 메모리 공간을 순회하며 flag 값이 ‘in-use’로 되어 있지 않은 객체들의 메모리를 해제한다. ‘in-use’ 상태가 아닌 객체들은 Root Set에서 접근이 불가능하기 때문이다.
  4. Sweep Stage가 끝나면 다음 Garbage Collection을 위해 모든 객체의 flag를 초기화한다.

-> Root Set이라는 말이 나오는데, Stack이나 Static 공간에 선언된 변수들의 모음 정도로 생각하면 이해가 쉽다.

--

--