[JVM] jcmd & jmap 힙 히스토그램 - 가장 많은 메모리를 소모하는 인스턴스 찾기

2021. 12. 16. 16:59 JAVA/JVM

Full GC가 발생하는 등 프로그램에 이상현상이 생기면 살펴봐야할 것 중에
메모리를 소모하는 클래스 중에 이상하게 많이 생성된 인스턴스가 존재하는지 살펴보는 것이다
예를들어, DB커넥션 객체가 1개만 있어야 하는데 여러개 생성되었는지 등 이상현상을 확인해봐야 한다

그래서 메모리 내의 인스턴스들의 점유 현황을 확인하는 방법이 필요하다
물론 GUI 로 쉽게 볼 수 있는 툴이 있지만, 공부겸.. jcmd, jmap을 이용해서 찾아보고자 한다

메모리 점유율 분석하기 - jcmd & jmap 힙 히스토그램

※ Heap Histograms 힙 히스토그램
힙 덤프 보다 어플리케이션 내의 많은 객체를 빠르게 살펴볼 수 있다. 
어떤 객체 타입이 메모리를 많이 소모하는지 빠르게 파악하는데 유용하다. 

1. jcmd  힙 히스토그램
$ jcmd {pid} GC.class_histogram
- 가장먼저 [ Full GC ] 를 발생시켜서 살아있는 인스턴스의 점유율만 보여준다.

위와 같이 메모리 내에 살아있는 인스턴스 개수들을 볼 수 있다.

[C : Character 배열을 의미
[B : Byte 배열
[I : Integer 배열
위와 같은 것들이나, java.util.* , sun.util.*과 같이 잘 동작한다고 믿을 수 있는 것들은 확률이 적으므로,
먼저 내가 생성한 패키지에 속한 클래스의 객체를 grep을 사용해서 살펴봐야 한다

아래는 테스트로 만든 어플리케이션이다. 어플리케이션의 패키지는 test.histogram이다. 해당 패키지 명으로 grep을 이용해서 어플리케이션에서 사용하는 객체들의 점유율을 볼 수 있다. 

$ jcmd {pid} GC.class_historgram | grep histogram


2. jmap  힙 히스토그램
$ jmap -histo {pid} 
$ jmap -histo:live {pid} // Full GC 발생
마찬가지로 힙에서의 메모리 점유율을 보여주는데, jcmd와의 차이점은 죽은 객체까지 포함한다. (GC를 동반하지 않음)
만약, jcmd처럼 GC를 발생시키고 싶으면 :live 옵션을 붙이면 Full GC가 발생한다.


jcmd histogram VS jmap histogram
두 개의 차이점은 jcmd는 살아있는 인스턴스, jmap은 죽은 인스턴스들도 보여준다. 
jcmd는 인스턴스들의 점유율을 확인하기 전에 GC를 한번 발생시킨다. 그래서 더이상 레퍼런스가 없는 죽은 객체들이 보여지지 않는다. 하지만 jmap은 GC 를 발생시키지 않고, 현재 메모리에 있는 살아있는 또는 죽은 객체들을 모두 보여준다.

jcmd를 하고 jmap을 연달아 실행하면 당연히 결과가 똑같아 질것이다.
jcmd에서 GC를 발생시켜서 죽은 객체들을 모두 없앴기 때문에 jmap에서도 살아있는 인스턴스만 나온다.

하지만 3번째 커맨드 라인에서 보듯이, jmap을 먼저 실행시키면 죽은 객체 Person이 3개가 나오고 그 다음 jcmd에서는 나오지 않는 것을 볼 수 있다.

결국은 jcmd, jmap 히스토그램 모두 메모리 점유율을 확인하는 동작은 같으나, jcmd는 동작 전에 GC 를 한번 발생시키는 것만 다르다. 

※ 실제로 jcmd 가 GC 를 발생시키는지 확인하고 싶은 경우 
$ jmc 
Java Mission Control을 실행시켜서 메모리 탭을 보면, GC 개수를 볼 수 있다. 
jcmd를 실행할 때 마다 카운트가 1씩 증가하는 것을 볼 수 있다. 
게다가.. Heap Histogram을 기본적으로 제공해준다.
여기서 확인해도 되지만, 커맨드라인으로 하면 grep과 같이 사용할 수 있는 것이 편하고 좋은 것 같다


※ 주의!!
힙 히스토그램을 생성하려면 어플리케이션에 부하가 발생한다.
어플리케이션에서 통계정보를 받아야하기 때문이다.
실제로 동작하는 프로덕션에서는 절대로 사용하지 말 것