System.gc 함수를 쓰지 말라는 이야기는 많이 듣곤 합니다. 왜 그럴까요?

 


 예제 프로그램을 보겠습니다.

 

 

 class A가 있고, 이 클래스 내부에는 finalize가 오버라이딩 되어 있습니다. 이 메서드 안에 들어오면 shared.count가 하나 증가합니다.  정리를 하면, 객체 A가 정리가 되면, shared 클래스 내부에 있는 클래스 변수인 count가 하나 증가합니다. 그리고 class A 안에는 int형 배열 10개가 있습니다.

 

 

 이제 Main 클래스의 main 메소드를 보겠습니다. 크게 어려운 것은 없어 보입니다. 10개의 객체 A 참조값을 저장할 수 있는 배열을 선언합니다. 그리고, 2만번의 loop를 돌 때 마다, 배열 A에 새로운 A의 참조값 10개를 넣습니다.

 

 이를 그림으로 표현하면 위와 같습니다.

 

 

 다음에, loop가 한 번 돌고 arr에 새로운 A의 참조값을 넣으면, 기존에 가르키고 있었던 객체들은 가비지가 될 겁니다.

 

 

 

 왜냐하면, 루트로부터 도달할 수 없기 때문입니다. 그러면, System.gc는 대체 무엇을 하는 친구일까요? 공식 설명을 보겠습니다.

 

 

 조금 어려운 문구들이 나오는데요. expend effort는 노력을 기울이다. 정도로 보시면 됩니다. 그리고 toward가 나왔는데, 이건 잘 모르겠습니다. 그 다음을 보면, recycling unused objects라고 되어 있는데요. 사용하지 않는 객체들을 재활용하는 것. 정도로 보시면 됩니다. 그러면, 사용하지 않는 것들을 재활용 하는 목적으로, 노력을 기울이다 정도로 해석하시면 됩니다. 이게 맞는 해석인진 모르겠네요.

 

 그리고, suggest라는 것이 나오는데, 이건 제안하다. 정도로 보시면 됩니다. 그러면, 사용하지 않는 가비지들을 수거할 것을 제안하다. 정도로 해석하면 되는데, 이것은 finalize와 같은 정리 작업이 실행이 100%가 된다는 뉘앙스와는 약간 다릅니다.

 

 

 

 실행 결과를 보면, 20만개의 A 객체의 finalize가 모두 정리가 되지는 않았습니다. 그런데, 사실 이건 보통의 문제고, 더 심한 문제는, 성능이 저하된다는 것입니다.

 

 

 

 생각보다 상당히 오래 걸리는 것을 확인할 수 있습니다. 왜 이런 걸까요? 필요 없는 작업을 수행하기 때문입니다. gc를 수거해야 한다면 어떻게 해야 할까요? 새로운 객체 연결 관계가 계속 추가되고, 삭제되는 경우에. 사실 제일 간단하게 생각할 수 있는 방법 중 하나는, Root로부터 탐색을 돌려서, 도달 가능한 객체만 빠르게 체크하고, 그렇지 않은 것들을 쓸어버리는 방법이 있을 겁니다.

 

 

 그렇게 하려면 전체 객체들을 모두 보아야 합니다. 물론 더 빠른 방법들이 있을 겁니다만. 변하지 않는 사실은 루프를 한 번 돌 때 마다, 쓸데없는 작업을 계속 한다는 것입니다.

 


 매 루프마다 System.gc를 호출하지 않으면 어떨까요?

 

 

 해당 부분만 주석 처리하고 실행해 보겠습니다.

 

 

 20만. 여기까지 보면, 출력 값의 차이라고만 생각하실 수도 있습니다.

 

 

 그런데, 퍼포먼스 마저도 차이가 꽤 나고 있습니다. 이는, 쓸모 없는 연산을 하지 않기 때문이기도 하고, 가비지를 탐색하고, 정리하는 작업이 꽤 무거운 작업이기 때문입니다. 결론. 이 메서드는 오버헤드도 크고, 그냥 gc에 맡기는 게 좋다. 정도로 정리하시면 되겠습니다.