Java에는 가비지 콜렉터가 있습니다. 이것이 어떻게 동작하는지는 추후에 쓸 기회가 있을 듯 싶습니다. 여기에 등장하는 용어들이 무엇인지에 대해서 간단하게 짚고 넘어가 보도록 하겠습니다. Reference 클래스는 사실, gc를 설명하기 위해 굉장히 중요한 키워드 중 하나이지만, 여기에서는 다루지 않겠습니다.

 

 먼저 Reachability가 뭘까요? 도달이 가능하다는 것입니다. 그러면 Reachable Object는 도달 가능한 객체 정도로 해석하면 될 겁니다. 어디에서부터 도달 가능한 것일까요?

 

 

  Root로부터 도달 가능한 Obj들을 Reachable하다고 합니다. 이 Root에 대한 설명은 링크를 보시면 나와 있는데요. 1번째 문장에서,  accessible from outside heap 이라는 문구가 보입니다. 사실 여기나, 여기를 찾아봐도, 공통적으로 많이 보이는 속성은, 힙이 아닌 다른 어딘가에서 접근 가능한 object이다. 정도인 듯 싶어요.

 

 


 이 Root에는 대표적으로, local 변수, Thread, System class 등이 있어요. 예를 들어 foo 메서드 안에, 지역 변수 a가 있었다고 해 봅시다. foo를 호출을 하게 되면, stack 영역에 지역변수 a가 할당되게 됩니다.

 

 

 그러면, 이 영역은 Heap 이외의 영역인가요? 그리고 a는 JVM이 들고 있나요?

 

 

 이 a가 obj를 가리키고 있다고 해 봅시다. 그러면, 이 때 obj는 heap 바깥에서 접근할 수 있다고 할 수 있나요? 네. stack 영역에 있는 지역변수 a에 접근하면 obj에 대한 어떠한 정보를 얻을 수 있고, 그것을 통해서 obj로 접근할 수 있습니다. 그런데 이 지역변수 a가 obj가 아닌 obj2를 가리키게 했다고 해 봅시다.

 

 

 그러면 heap 바깥에서, obj로 접근할 방법이 있나요? obj2로 access 할 방법은 있습니다만, obj로 접근하는 방법은 없습니다. 즉, obj1은 reachable에서 unreachable이 되었습니다. 이것을 그림으로 다시 그려보면 아래와 같습니다.

 

 

 root에서 obj1로 접근하는 길이 있었습니다. 고로, obj1은 Reachable 합니다.

 

 

 a가 다른 객체 obj2를 가리키도록 해 봅시다. 그러면, root에서 obj2로 가는 방법은 있지만, obj1로 가는 방법은 없어요. 루트에서 가는 방법이 없습니다. 도달 가능하지 않은 객체를 가비지라고 합니다.

 

 

 16번째 줄을 수행했을 때, 벌어진 상황입니다. 이제, foo 함수가 끝나면 어떻게 될까요?

 

 

 root에서 obj1, obj2로 접근할 수 있는 방법이 없습니다. 지역변수 a는, 더 이상 visible 하지 않기 때문입니다. foo에 대한 것이 정리가 되었기 때문에, 군청색으로 칠한 영역은 더 이상 visible 하지 않습니다. 따라서, root에서 obj1, obj2에 접근할 수 있는 방법이 없게 됩니다.

 

 


 도달 가능하다. 그렇지 않다에 대한 이야기를 했으니, 가비지를 어떻게 제거하느냐에 대한 이야기도 아주 간단하게 이야기를 해 봅시다. 사실, 이 알고리즘도 여러 개가 있습니다. 그것들에 대해서 파헤치는 것도 재밌는 일 중 하나일 겁니다. 그 중에서, 이해하기가 그나마 쉬운 mark하고 sweep 하는 것만 언급하도록 하겠습니다. 사실 bfs나 dfs와 같은 그래프 탐색에 대한 이해가 있다면, 손쉽게 구현할 수 있기도 하고요. 먼저, a가 b를 참조한다는, 다음과 같은 그래프로 나타낼 수 있습니다.

 

 

 당연하게도 이 때 b에서 a로 가는 간선은 그려지지 않습니다. b가 a를 참조하는 것이 아니기 때문입니다. root로부터 도달 불가능하다. 라는 것을 어떻게 체크하면 좋을까요? root로부터 bfs나 dfs를 돌렸을 때, 방문하지 않은 객체들이 있다면, 이들은 모두 도달 불가능 할 겁니다. 예를 들어, 참조 관계가 아래와 같이 있다고 해 봅시다.

 

 

 그러면 어떻게 하면 될까요? root1과 root2로부터 탐색을 시작합니다. 이 때, 도달 가능한 객체는 아래와 같습니다.

 

 

 a는 roo2에서 바로 가거나, root1에서 b, a로 갈 수 있습니다. b는 root1에서 b로 갈 수 있습니다. c는 어떻게 하더라도 갈 수 없어요. 갈 수 있는 곳을 mark, 혹은 visit 처리를 한다면, 가지 못하는 곳은 visit를 하지 않았을 겁니다. 예를 들면 c입니다. 이들을 sweep 하면 됩니다.