대부분이 OOME 등이 발생하면 이런 쉬운것(메모리 분석)도 안해보고 왜 그러지, 왜 그러지 하면서 이것저것 손보는데 시간만 보내기십상이다. 

일단 이거 엄청 간단하니깐 일단 이렇게부터 해보구 삽질을 했으면 좋겠다.-_- 여우같은 마눌 보러 가야징


JVM 메모리 분석을 위한 과정은 다음과 같다.

  1. 분석할 프로세스 ID 조회
  2. 메모리 힙덤프 추출
  3. 힙덤프 분석

분석할 프로세스 ID 조회

프로세스 id 조회는 ps - ef | grep 찾을 놈 관련된 텍스트 와 같이 하면 아래와 같이 귀찮게 나올 수 있다. 이건 뭐, 얼마 안된 케이스긴 하지만. 여튼 그렇다.

 

shell.sh
[freshkorea@multi-003 ~]$ ps -ef | grep freshkorea
root     12044  3911  0 15:16 ?        00:00:00 sshd: freshkorea [priv]
1493     12105 26169  0 15:16 ?        00:00:00 /usr/local/apache2/bin/httpd -k start -f /usr/local/apache2/conf/hosting/freshkorea/httpd.conf
1493     12205 12044  0 15:17 ?        00:00:00 sshd: freshkorea@pts/2
1493     12655 26169  0 15:17 ?        00:00:00 /usr/local/apache2/bin/httpd -k start -f /usr/local/apache2/conf/hosting/freshkorea/httpd.conf
1493     12925 26169  0 15:17 ?        00:00:00 /usr/local/apache2/bin/httpd -k start -f /usr/local/apache2/conf/hosting/freshkorea/httpd.conf
1493     13004 26169  0 15:17 ?        00:00:00 /usr/local/apache2/bin/httpd -k start -f /usr/local/apache2/conf/hosting/freshkorea/httpd.conf
1493     13005 26169  0 15:17 ?        00:00:00 /usr/local/apache2/bin/httpd -k start -f /usr/local/apache2/conf/hosting/freshkorea/httpd.conf
1493     14438 12206  0 15:17 pts/2    00:00:00 grep freshkorea
root     26169     1  0 Jan30 ?        00:05:26 /usr/local/apache2/bin/httpd -k start -f /usr/local/apache2/conf/hosting/freshkorea/httpd.conf
root     27019  3911  0 15:11 ?        00:00:00 sshd: freshkorea [priv]
1493     27051 27019  0 15:11 ?        00:00:00 sshd: freshkorea@notty
1493     29991     1  1 Mar29 ?        00:44:22 /usr/local/jdk/bin/java -Djava.util.logging.config.file=/home/hosting_users/freshkorea/tomcat/conf/logging.properties -XX:MaxPermSize=128m -Xmx128m -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/home/hosting_users/freshkorea/tomcat/endorsed -classpath /home/hosting_users/freshkorea/tomcat/bin/bootstrap.jar -Dcatalina.base=/home/hosting_users/freshkorea/tomcat -Dcatalina.home=/home/hosting_users/freshkorea/tomcat -Djava.io.tmpdir=/home/hosting_users/freshkorea/tomcat/temp org.apache.catalina.startup.Bootstrap start

그럴 땐 JDK/bin 밑에 있는 jps 명령을 이용하자.

jps 는 현재 계정의 java 프로세스만 보여준다.

 

shell.sh
[freshkorea@multi-003 ~]$ jps
29991 Bootstrap
14957 Jps

와우! 바로 딱 나온다. 

tomcat 은 실행시 bootstrap.jar 를 이용하기 때문에 bootstrap 으로 나온다.
이거 가지고 잘 모르겠다면, 'jps -mlv' 이렇게 쳐보라. -_-)b

메모리 힙덤프 추출

보통 힙 덤프 뜰 땐 kill -3 <pid> 를 사용한다. 허나, 그렇게 해도 안찍힐 경우에 jmap 을 이용해 보자.
jmap dump:format=b,file=<output filename> <pid>
메모리 분석을 위해서 fomat=b 을 줌으로써, 바이너리 파일로 생성해야한다.

 

healdump.sh
[freshkorea@multi-003 ~]$ jmap -dump:format=b,file=heap.hprof 29991
Dumping heap to /home/hosting_users/freshkorea/heap.hprof ...
Heap dump file created
[freshkorea@multi-003 ~]$

힙덤프 분석

힙덤프는 분석 툴을 이용해 분석하는게 편하다. eclipse 의 mat 은 공짜이면서 leak 원인까지 찾아주는 기능이 있다.

http://www.eclipse.org/mat/

받아서 설치하자. (그냥 압축 풀면 끝이다.)

설치 후에 Fille -> Open -> Heap dump 를 하면 결과가 다음처럼 대충 나온다. 

 

 

그리고 Details 를 클릭해서 들어가보면 다음과 같이 나온다. 

여기선 hashMap 이 아주 무식하다는게 드러난다. 

 

이제 소스를 보면 왜 이런 참상이 생겼는지 알꺼다.

이렇게 OOME 가 엄청 간단하게 해결되었다.


특정 엔티티의 갯수가 많고, 그것을 한번에 처리하려고 하는 경우에 Query 나 Criteria 를 이용해 .list() 를 할 경우에, OutOfMemoryError(OOME) 가 나기 쉽상이다.

BadExample.java
Query q = getSession().createQuery("FROM "+Contents.class.getName())
                    .setReadOnly(true);
List<Contents> contentsList = q.list();
for (Contents contents : contentsList) {
    // TODO work here
};

이것을 처리하기 위해서는 scroll 을 이용하여 특정 데이터씩만 조회하여 처리한 후, 세션을 클리어해주면 된다.

GoodExample.java
ScrollableResults results = getSession().createQuery("FROM "+Contents.class.getName())
    .setReadOnly(true).setCacheable(false)
    .setCacheMode(CacheMode.IGNORE)
    .setFetchSize(10)
    .scroll(ScrollMode.FORWARD_ONLY);
while (results.next()) {
    Contents contents = (Contents)results.get()[0];
    // TODO work here  
    getSession().clear();
}

중요한 부분은 다음 포인트다.

  • list 대신 Cursor 를 이용하는 scroll 메서드를 이용한다.
  • Fetch 사이즈를 적절히 설정해서 한번에 처리할 레코드 수를 설정한다.
  • 작업 처리 후에는 Session 을 clear 한다.

이렇게 하면 메모리 부족 현상에서 해결된다.

하이버네이트를 혐오하는 사람들 중의 생각 하나가, "메모리에 다 올려놓으니깐 못 써먹는다" 인데 그럴리가 있니?

java.lang.OutOfMemoryError: Java heap space


maven 을 별도로 세팅하지 않고 대체로 war 패키지를 deploy 할 경우에 흔히 일어나는 현상이다.
war 의 경우 파일의 사이즈가 큰 편인데 jvm 옵션을 주지 않으면 완성된 war 를 deploy시 java.lang.OutOfMemoryError 가 나곤 한다.

이러한 일련의 문제는 jvm option 을 주므로서 해결할 수 있는데, 환경 변수에 MAVEN_OPTION 을 등록하면 된다.

나는 다음의 JVM_OPTION 을 MAVEN_OPTION 으로 등록하였다.

-XX:PermSize=32m -XX:MaxPermSize=128m -Xms128m -Xmx512m



+ Recent posts