Java, Heap Dump 파일 분석하기!

Develop/Java 2021.03.16 댓글 PSJco
728x90
반응형

안녕하세요, PSJ입니다. 

오늘은 Java Application을 운영하다 보면 가끔 발생될 수 있는 메모리 누수나, OOM(Out Of Memory Error)을 Heap Dump 분석을 통해 확인하는 방법을 포스팅하려 합니다. 

 

사실 어제 팀에서 운영 중인 웹 어플리케이션의 서버 한대에 컨테이너가 Down 되는 문제가 발생했습니다. 

원인을 확인하는 과정에 Heap Dump 파일을 분석할 일이 생겼고, 넘어진 김에 쉬어가라고.. 이번 기회에 정리해 보려고 합니다. 

 

우선 메모리 누수의 경우 개발 단계에서 관련 테스트를 충분히 수행하지 않는 경우 잡아내기 어려운 문제입니다. 

Production 환경과 개발 환경의 데이터 및 환경의 차이로 Develop에서는 정상 작동하던 것도 운영 환경에서는 오류가 발생할 수 있는 것이죠. 

로그 분석

Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded 
	at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:399)

로그를 뒤지다 보니 위와 같은 오류가 발생했었습니다. 컨테이너가 내려가기 직전에 발생된 점을 보아 저 문제로 컨테이너가 내려간 것으로 판단했습니다. 

 

"GC overhead limit exceeded" 오류는?

JVM에서 어플리케이션이 GC(Garbage Collector)를 수행하는데 너무 많은 시간이 걸린다는 오류. 기본적으로 JVM은 GC를 수행하는데 총시간의 98% 이상을 소비하고 GC 후에 Heap의 2% 미만만 복구되는 경우 이 오류를 발생된다. 

GC를 수행한 결과 Overhead가 많다는 말은 메모리가 비효율적으로 많이 사용되고 있다는 이야기 입니다. 혹시나 WAS에서 떨어진 Heap Dump가 존재하는지 Dump파일 경로를 보니, 아니나 다를까 Dump 파일이 존재했다. (JEUS의 경우 Dump 파일 생성 시 WAS 컨테이너가 FAIL 상태로 아무런 동작도 수행하지 못했습니다. 아직 Tomcat이나 다른 WAS는 비교해 보지 못함, 또한 Dump 파일 생성 시 컨테이너가 FAIL 상태가 되는 부분은 추후 개발 서버에서 추가 확인해 볼 예정) 이렇게 Dump 파일을 떨구느라 컨테이너가 Down(FAIL)된 것으로 판단됩니다. 

Heap Dump 분석 

Dump 파일의 경우 전용 분석툴이 여러 가지 있지만, Eclipse 기반으로 동작하는 "MemoryAnalyzer"라는 툴을 이용했습니다. (Eclipse MarketPlace 에서 Plug-in으로도 설치 가능) 

 

Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation

The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 375 open source projects, including runtimes, tools and frameworks.

www.eclipse.org

설치 후 실행하면 이렇게 Eclipse 스러운 화면이 뜹니다. 

"Open a Heap Dump" 메뉴를 눌러서 Dump 파일을 열어줍니다. 

Dump 파일의 사이즈에 따라 여기서도 OOM이 발생될 수 있습니다. (ex: Heap Dump 파일 사이즈 1.03GB 일 경우 오류가 납니다) 이때는 당황하지 마시고, MemoryAnalyzer 실행시 JVM 메모리를 늘려줍니다. 실행 폴더로 가보시면. ini 파일이 있습니다. 그걸 열어서 "-Xmx1024 m" JVM 옵션을 "-Xmx2048 m"으로 변경해 주면 되겠죠. 

이제 실행해서 Dump 파일을 로드하면 아래와 같이 통계가 보여집니다. 

 

총 1GB의 메모리중 710MB가량을 하나의 Thread에서 사용하고 있었습니다. "Dominator Tree"로 자세히 살펴보겠습니다. 

13,845,150개 String 객체가 ArrayList에 담겨있습니다. 총 90%가 넘는 메모리를 점유하고 있으니 당연히 문제가 되겠죠. 여기서 로직상 많은 메모리 사용이 필요한 어플리케이션에 경우에는 당연히 JVM 메모리를 늘릴 필요가 있지만, 일반적으로 1,400만 개가량의 ArrayList가 메모리에 상주해야 하는 로직은 작성하면 안 되겠죠? ^^; 원인 파악은 끝났습니다. (각 객체별 저장된 Data까지 볼 수 있어서 저걸 보고 원인을 못 찾을 수 없을듯)

 

이제 로직을 수정해야 합니다. 확인해 보니 Data를 조회할때 인수에 null 이 들어가면서 필터링되지 않은 전체 Data가 조회되어 발생한 문제였습니다. (null check를 생활화합시다!)

 

자, 오늘은 JVM Heap Dump 파일을 분석하는 방법을 알아봤습니다. 막연히 해보지 않은 분들은 뭔가 대단한 기술이 필요할 것 같지만, 툴을 이용하면 누구나 쉽게 할 수 있습니다. 

 

절대 겁먹지 마시고, Dump 파일이 떨어지면 꼭 분석을 통해 원인을 파악하고 문제를 해결하시기 바랍니다. 

 

이상입니다. 

 

 

공감과 댓글은 큰 힘이 됩니다.

 

728x90
반응형

댓글