Memory Analyzer 로 heap dump 해석하기


- その1: 서버 백업
- その2: 프리즈의 원인과 가비지 콜렉션
- その3: 사무라이로 heap 사용량 확인
- その4: 릭 개소를 확인하는 여러가지 방법
- その5: Memory Analyzer 로 heap dump 해석

Eclipse Memory Analyzer(MAT)를 사용하여 인스턴스가 얼마나 메모리를 잡아먹고 있는지를 알 수 있습니다.

용어해석
 *   Shallow Heap:  1개의 오브젝트가 소비하는 메모리. 1개의 참조에 대해 32 bit가 소비된다.
                               예를 들면, Integer에서는 4바이트, Long에서는 8바이트
 *  Retained Heap:  GC에 의해 제거되는 일련의 오브젝트가 사용하는 Shallow Heap의 합계.
                               오브젝트의 트리가 직접 사용하고 있는 메모리

heap dump는 JDK 에 들어있는 jmap 커맨드로 뽑아냅니다.

jmap -heap:format=x [pid]  또는  jmap -heap:format=b [pid]  의 형태로 실행하면 heap dump를 xml 형식, 또는 바이너리 형태로 추출합니다.
보통은 쌩으로 heap dump를 사람이 읽는것은 힘들기 때문에 xml 형식은 사용하지 않으며, 바이너리로 출력합니다.
바이너리 형식의 heap dump는 Eclipse Memory Analyzer 라는 툴로 해석할 수 있습니다.
Eclipse Memory Analyzer 는 SAP 가 개발한 Eclipse RPC 를 베이스로 멀티플레이트 폼 어플리케이션입니다.
꽤 괜찮은 녀석이죠.


Memory Analyzer 를 기동한 샷~!

Memory Analyzer 를 기동하면 heap dump를 간단히 읽을 수 있습니다.
[File > Open Heap Dump...] 로 폴더를 열어, heap dump를 선택합니다.


heap dump 를 열면 요롷게 보이죠

메모리 Leak 문제의 조사는 크게 2가지가 있습니다.

 1. heap 에서 차지하는 비율이 큰 오브젝트를 찾는것, 이것은 heap dump를 한개를 열어보면 해석이 가능합니다.
 2. 어플리케이션을 작동시키는 과정을 간단하게 단조증가시키면서 오브젝트를 발견하는 방법.
    구체적으로 heap dump 를 2회이상 취득해서 차분을 구하여 해석을 합니다.

"1." 에서 덩치가 큰  오브젝트 또는 대량으로 존재하는 오브젝트를 보는것으로 원인을 분석해 봅시다.
하지만 그런 오브젝트는 보통 캐쉬로 되어있는것이 많습니다. 계속 증가되는 형태가 아닌 경우는 조사해도 별볼일 없습니다.
정말 릭 되어 있는 오브젝트를 확인하기 위해서는 "2." 에서 단조증가되고 있는 오브젝트 해석법으로 해야 합니다.


1. heap 영역을 선점하는 비율이 높은 오브젝트 확인법

heap dump를  Memory Analyzer 로 열어 표시되는 over view화면에서 "Leak Suspects" 링크를 클릭!

커다란 오브젝트를 대략적으로 판단해서 픽업해 줍니다.
「대략」적으로 나오는 그림이니 반드시 Leak되어진 오브젝트가 검출된다고는 할 수 없죠

Leak Suspects 를 열었을때

위에는 org.apache.jasper.runtime.BodyContentImpl 클레스의 인스턴스가 108 개로、합계419MB(heap中72.26%)를 차지하고 있네요.
org.apache 라는 패키지로부터 Apache 프로젝트의 컴퍼넌트에 존재하는 것을 알 수 있습니다. 그리고 jasper 라는 이름은 Apache 의 jsp 코드죠.
인스탄스의 수는 108 개로 아주 많은건 아니지만 하나하나 사이즈는 심상치 않네요.
하나의 인스턴스는 뭘 기록하고 있을까요
? 함 봅시다.
이번엔 dominator tree 뷰에서 오브젝트의 속을 까봅시다.
dominator tree를 여는것은 아래 그림처럼 툴바에서 선택합니다.

dominator tree를 여는 아이콘

dominator tree 를 열면 큰 오브젝트 순서대로 소트된 인스턴스 일람이 보입니다.
그리고 이것저것 인스턴스 트리를 전개하면, 보유하고 있는 필드와 속 알맹이를 확인할 수 있습니다.

dominator tree 표시

이번엔 dominator tree 에서、BodyContentImpl 에는 꽤나 사이즈가 큰 char 배열이 있는것을 알게 되었습니다.
Inspector 페인으로 간단한 배열의 속을 미리볼 수 있지만, 부분부분 밖에 볼 수 없네요.
하지만 파일로 저장하면 상세 하게 볼 수 있습니다.


오브젝트 알맹이를 파일로 저장


보존한 char 배열의 알맹이를 확인

알맹이를 view 커맨드로 확인하면 html 이 기록되는 것을 확인 할 수 있습니다.
좀 더 자세히 보면 ・・・・썩스 그다지 크지 않는 영단어가 <div class="trackbackheader">..</div> 라는 태그에 싸여져
대량으로 들어가 있습니다.

이건 트랙빽 스펌메일 이군요.
<img border="0" src="/jira/images/icons/document_exchange.gif" width=16 height=16 align=absmiddle> 의 요소가 보이는데
 
Jira 가 들어가 있네요.
Jira 는 자신의 오픈 소스 프로젝트의 버그 트랙킹에 사용하고 있는 Web 어플리케이션입니다.
Jira 를 사용하면 뭔가 JBoss Web(JBoss 내장의 Tomcat 베이스의 컨테이너)로 Leak 하는 것일지도...

다음은 BodyContentImpl 이외에도 증가하고 있는 오브젝트가 없는지, heap 덤프의 차분도 확인해 보기로 하겠습니다.



2. 단조증가되고 있는 오브젝트 확인
heap dump의 차분을 내는것은 heap dump를 두개 열어서 히스토그램 뷰(막대 그래프의 아이콘)가 열린 상태에서 좌우의 화살표를 누릅니다.

heap dump 비교

그러면 Select baseline 이라는 다이얼로그가 뜨는데 차분을 뽑고싶은 또다른 heap dump를 선택합니다.

Select baseline에서 차분을 보고싶은 대상의 heap dump를 선택

차분이 나오면, "Objects" 즉 인스턴스 수로 소트해 봅시다.
그럼 재미있는 오브젝트가 텨 나옵니다.
com.atlassian.trackback.Trackback 라는 오브젝트가 20,211개가 증가해 있는 것을 알 수 있겠습니다.

차분을 취득해서 나온 - com.atlassian.trackback.Trackback

atlassian 은 Jira 를 개발한 회사입니다. 트랙백 스팸메일을 대략으로 접수하서 트랙백을 표현하는 오브젝트가 증가하는것 같네요.

"com.atlassian.trackback.Trackback" 로 검색한 결과 leak에 관련된 정보는 없네요.

이번엔
"jira tomcat leak"로 검색! 빙고!
05.03.04 Tomcat 6.0 - JIRA 오브젝트 메뉴얼 (3.12.2版 - 최신) - Atlassian Confluence
Tomcat 의 메노리 설정을 수정

Tomcat는, 큰 JSP의 페이지를 리퀘스트 하면 메모리 leak 되어 버립니다.
이것을 피하기 위해서, bin/setenv.sh를 다음과 같이 설정해 주세요.

export CATALINA_OPTS="$CATALINA_OPTS -Dorg.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER=true"

제대로 문서에 써 있는 설정을하지 않았던 것이 원인이었습니다.
LIMIT_BUFFER 옵션에 대해서는 이하의 페이지로 도입의 경위가 자세하게 설명되고 있습니다.


Bug 37793 – org.apache.jasper.runtime.BodyContentImpl doesn't reset the 'cb' character array, causes memory leak

Jira 의 issue 는 여기문서 참조
[#JRA-10145] Set tomcat flag to not re-use buffers, as they result in OutOfMemoryErrors - Atlassian JIRA

아무래도, Tomcat 은 버퍼를 재이용하는 구조가 있는 것 같아서,
매우 큰 컨텐츠를 출력하면 버퍼로 heap가 넘쳐 버리는 것 같습니다.
이 기입 버퍼를 무진장하게 늘리지 않기 위한 옵션이 LIMIT_BUFFER군요.

즉, 이번 메모리 leak 문제의 발생 스텝은 이렇게 상상할 수 있습니다.
 1.  트럭 백 스팸메일을 대량으로 받아 특정의 페이지의 사이즈가 증가됨
 2.  검색 엔진의 클로러에 증가된 페이지를 열람한다
 3.  버퍼가 증가됨

서버는 Tomcat는 아니고 JBoss로 운용하고 있습니다만,
Web 컨테이너인 JBoss Web는 Tomcat  의 기반이므로 이 옵션이 제대로 효과가 있었습니다.

트랙백 오브젝트가 대량으로 있는 것은 좋은 상태인지는 모르겠습니다만,
원래 트랙백 기능은 현재의 곳에는 필요없기 때문에, 관리 화면으로부터 무효로 설정해 버렸습니다.


트랙백 기능을 OFF 로 설정

LIMIT_BUFFER 옵션의 설정과 트랙백의 OFF 설정을 실시하고 나서 1주간 이상 지납니다만,
현재의 heap 사용량은 일정하고 안정적으로 가동하고 있습니다.

메모리 릭의 트러블 슈팅은 차분히 원시 코드를 바라보거나
프로파일러를 사용해 현상을 재현시키는 조사 방법이 유명하네요.
그러나 heap 덤프나 Memory Analyzer를 사용하는 방법도 쉽게 접근 할 수 있을 것입니다.
메모리 릭 트러블은 장기간 가동하고 처음으로 나타나는 것도 귀찮은 문제입니다.
트러블이 발생하고 나서 당황하는 일이 없게, 미리 heap 덤프를 사용한 해석하는 연습을 해 두면 좋을것 같습니다.

번역하고나니 어설픈 부분도 있네요
출처: http://samuraism.jp/diary/2008/11/10/1226316240000.html

+ Recent posts