java gc log 를 출력해 봅시다.

코딩/Java 2021. 3. 22. 02:00

 어느 분하고, LinkedList 문제에 대해서 이야기를 나누다가, jvm 관련 이야기까지 나왔던 기억이 납니다. 그에 대해서, 어딘가에 정리한다 해 놓고, 깜빡 잊었는데요. 이제서야 정리해 봅니다. 저는 jdk8에서 프로그램을 실행하였습니다.

 


 jvm의 gc 로그를 출력하기 위해서는 option을 만져야 합니다. 

 

 

 Run에서 Edit Configurations에 들어가 보겠습니다.

 

 

 창이 하나 뜨는데요. 여기서 add Run Option을 눌러 보겠습니다.

 

 

 add VM options에 체크해 주겠습니다.

 

 

 그리고 -verbose:gc를 옵션으로 주겠습니다. 해당 옵션은 gc event를 출력하는 옵션입니다.

 

 

 

 이 옵션을 주고 실행시킬 프로그램은, 해시맵 hm에 Dog 객체 30만개를 넣고, 새롭게 생성되는 Dog 객체가 hm에 있는지 검사하는 프로그램입니다.

 

 

 Dog 객체는 다음과 같습니다. hashCode와 equals가 정의되어 있지 않습니다.

 

 

 Full GC와 GC 로그가 계속 찍히는 것을 알 수 있습니다. gc log를 출력하는 방법은 -verbose:gc를 VM option으로 주면 되겠군요. 이것은 단지, 기본적인 것들만 출력합니다. java option에 대해 설명하는 문서를 보시면, gc 로그를 출력할 때, 추가로 줄 수 있는 옵션들이 있음을 알 수 있습니다.

 

 

 이 중에 하나인 PrintGCDataStamps를 적용해 보겠습니다.

 

 

 그러면, gc가 일어난 시간을 출력해 줍니다.

 


 아래 프로그램을 생각해 보겠습니다.

 

 

 Main 클래스를 살짝 바꾸어 보았습니다. for loop를 조금 바꾸어 보았는데요. 기존에 hm.containsKey를 호출할 때에는 계속 새로운 Dog 객체를 생성해서, 그것을 키 값으로 삼았습니다. 그런데, 이 프로그램은 다릅니다. 이미 생성된 Dog 객체를 키 값으로 삼았습니다. 로그에 찍힌 결과를 보겠습니다.

 

 

 그러면, 4번 정도 GC가 호출이 되고, 그 다음에는 계속 for loop를 돌아도 로그가 찍히지 않았음을 알 수 있습니다. 즉, gc는 필요하다고 생각이 들 때만 일어난다는 것을 알 수 있습니다. 새로운 객체도 생성되지 않고, 메모리가 부족하지도 않은데, 무거운 연산을 수행할 필요는 없는 셈입니다.

 

 

 꽤 오래 전에 이 글에서 언급한 finalize 메서드를 생각해 보겠습니다. 제가 글의 제목에서, 언제 실행될 지 알 수 없다고 했습니다. 그런데, 레퍼런스의 설명에 보면, gc가 이 오브젝트들을 정리할 필요가 있을 때 호출한다고 되어 있어요. 그리고 조금 더 읽어 보면, root로부터 도달 불가능한 것을 정리한다는 것도 알 수 있습니다. 사실 저 글만 보면, 언제 실행될 지 알 수 있어 보이긴 하는데..

 

 

 그런데, 생각해 보면, 6번째 줄에서 새로 생성한 Dog 객체는 8번째 줄에 오면, root로부터 도달이 불가능하다는 것을 알 수 있습니다. 그럼에도, gc log에 찍히는 게 없음을 알 수 있습니다.

 

 

 이는 정리할 필요성이 없었기 때문입니다. 그래서, finalize 안에서만 파일을 닫는 로직을 태우는 것은, finalize만 믿는 것은 그리 좋지 않습니다. 객체 자체는 메모리에 할당할 수 없을 때 정리하면 그만이지만, file 데스크립터와 같은 자원들은 그렇지 않을 수도 있기 때문입니다. 파일을 열고 닫는 것은 try resource를 쓰시면 됩니다.