java.lang.OutOfMemoryError
이 문서는 기술지원 또는 개발 시 java.lang.OutOfMemoryError 를 만났을 때 원인 파악 및 해결방안 입니다.
java.lang.OutOfMemoryError 에는 크게 2가지 패턴이 있다고 볼 수 있습니다.(전적으로 개인적인 생각이지만..^^)
Java의 heap 메모리가 정말로 Full 되서 나는 종류가 있고 그렇지 않은데도 나는 종류가 있습니다.
그 둘 중에 어는 것 인지부터 가려내는 것이 가장 먼저 선행되어야 합니다.
Java 가상머신에는 메모리 구조가 여러단계로 나뉘어 져 있으므로 어느 영역에 의해 java.lang.OutOfMemoryError 가 나는지 알기 위해 JAVA OPTION으로 싸이트가 안정화 되기 전까진 –verbosegc 또는 -XX:+PrintGCDetails 옵션을 추가해 놓는 것이 java.lang.OutOfMemoryError 났을 때를 대비해 좋은 습관이라 할 수 있습니다.
-verbosegc 또는 -XX:+PrintGCDetails 옵션으로 로그에 남는 heap 메모리 정보를 본 후 java.lang.OutOfMemoryError 가 날 때 heap이 꽉 차서 나는 것인지 아닌지 부터 판단합니다.
1. Heap Memory가 Full 차지 않았을 때
1) Perm 영역이 full 되는 경우
Permanent generation is full...
increase MaxPermSize (current capacity is set to: 134217728 bytes)
[Full GC[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor31282]
810207K->802132K(1013632K), 8.3480617 secs]
<GC: 2 4 2465625.831280 10348 0 31 113802808 105534632 286326784 0 0 35782656 715849728 715848840 715849728 134217720 134023296 134217728 8.348677 8.348677 >
Passwd Check =============
<
java.lang.OutOfMemoryError
위와 같은 case 인 경우네는 Java 가상머신중에 Perm 영역이 full 차는 경우 입니다.
Perm 영역에는 class object 및 관련된 meta data가 로드되는 곳인데 싸이트가 매우 많은 수의 class를 사용하는 경우 늘려줘야 하는 case도 있습니다.
얼마가 적정한 사이즈란 정답이 없는 것 같고 max가 계속 늘어나지 않고 일정한 사이즈를 유지 하면 됩니다.
위 에러가 났을때는 -XX:MaxPermSize=256m 옵션으로 사이즈를 적당하게 늘려주는게 방
법이며 늘려주었는데도 불구하고 Full 차는 시간만 늘어날 뿐 계속 사이즈가 늘어난다면 이 영역은 일반 비즈니스 프로그램으로 핸들링 할 수 없는 영역이므로 WAS 제품의 버그 및 jdk 버그로 보는 것이 일반적입니다.
2) MAXDSIZ 사이즈 관련
- Exception in thread "CompileThread0" java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate
Possible causes:
- not enough swap space left, or
- kernel parameter MAXDSIZ is very small.
- java.lang.OutOfMemoryError: unable to create new native thread
위 두 에러가 나는 이유는 Data 사이즈가 Full 차서 더 이상 메모리 할당을 할 수 없다는 java.lang.OutOfMemoryError 입니다.
Jdk도 내부적으로 c 라이브러리를 쓰고 jni를 통해서 c프로그램을 호출할 수 도 있는 환경에서 Data 영역이상으로 메모리 사용 시 위 에러를 만날 수 있습니다.
Heap 영역에는 java heap과 C heap이 있는데 C heap이 Data 메모리 영역에 영향을 미치는 것으로 보이며 보통 C의 전역 변수들이 잡히는 영역입니다.
위 현상을 만났을 때는 hp os의 MAXDSIZ가 너무 작게 잡혀있지 않은지 확인 후 적당한 크기로 늘려 줘야 합니다.
Glance 에서 shift+m 을 누른 후 jvm pid를 누르면 java가 사용하는 Data 사이즈를 모니터링 할 수 있습니다.
모니터링을 통해 적정한 사이즈로 MAXDSIZ를 늘려주어야 하며 만일 늘려 준게 에러 발생의 시간만 지연 시킬 뿐 계속 사용량이 늘어난다면 이는 사용하는 c라이브러리 쪽에 메모리 릭 버그가 있는 것이므로 c라이브러리를 체크 하셔야 합니다.
java.lang.OutOfMemoryError: unable to create new native thread
이 경우는 프로그램에서 Thread pool 프로그램 실수로 필요이상으로 쓰레드가 생성되는 경우도 과도하게 메모리를 사용할 수 있으므로 jvm 쓰레드 덤프를 통해 과도한 쓰레드가 생성되지 않았는지도 확인해 보셔야 합니다.
2. Heap이 Full 찾을 때
이 경우는 java가 사용하는 heap 영역이 Full 되서 java.lang.OutOfMemoryError 가 나는 경우 인데 두 가지 패턴이 있습니다.
순간적으로 대량의 데이터를 메모리에 올리는 프로그램이 실행되어 문제를 야기 할수 있으며 다른 한 가지는 조금씩 메모리 릭이 발생하여 점차적으로 메모리가 쌓여 가는 경우 입니다.
두 가지 중에 어느 것인지 구별하는 것이 중요하며 이를 위해서도 마찬가지로 -verbosegc 또는 -XX:+PrintGCDetails 로그를 통해 순간적으로 메모리가 차는 것인지 조금씩 메모리가 차는 것인지를 확인하셔야 합니다.
1) 특정 프로그램의 특정시점의 과도한 메모리 사용에 의한 경우
특정 프로그램이 과도하게 heap 메모리 이상 메모리를 사용하면서
java.lang.OutOfMemoryError가 발생하는 경우는 이 에러가 발생하는 시점의 쓰레드 덤프를 통해 어느 프로그램인지 쉽게 찾을 수 있습니다.
쓰레드 덤프에 메모리를 쓸만한 프로그램은 어느 정도의 자바프로그램 경험이 있으면 찾을 수 있습니다.
Ex)
"ExecuteThread: '36' for queue: 'default'" daemon prio=10 tid=0x0048e7b0 nid=48 lwp_id=4139729 runnable [0x23f32000..0x23f30500]
at java.net.SocketInputStream.socketRead(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at oracle.net.ns.Packet.receive(Unknown Source)
at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:718)
at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:690)
at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:373)
at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1405)
at oracle.jdbc.ttc7.TTC7Protocol.fetch(TTC7Protocol.java:889)
- locked <0x35e8e3b0> (a oracle.jdbc.ttc7.TTC7Protocol)
at oracle.jdbc.driver.OracleResultSetImpl.next(OracleResultSetImpl.java:242)
- locked <0x36f66c98> (a oracle.jdbc.driver.OracleResultSetImpl)
at weblogic.jdbc.pool.ResultSet.next(ResultSet.java:180)
at Dcard.AAA.ejb.monitor.member.wbbb.WACBean.getInfoList(WACBean.java:5789)
java.lang.OutOfMemoryError 가 날 상황에 쓰레드 덤프를 두 세번 떠서 같은 쓰레드 번호로 위 같은 로직이 오래 결려있다면 이는 프로그램에서 while(rs.next()) 를 오랜 기간 돌면서 메모리에 DB로부터 읽어서 올리고 있다는 것을 의미 합니다… 대량 데이터 조회가 일어나는 경우인 것 입니다.
이런 식으로 특정 프로그램의 특정 시점에 과도한 메모리 사용으로 인한 java.lang.OutOfMemoryError 에러는 쓰레드 덤프를 통해 찾을 수 있습니다.
-- 물론 2번에서 설명 할 heap dump를 통해서도 찾을 수 있습니다.
2) 메모리 릭에 의해 조금씩 메모리가 쌓인 경우
JVM위 에서 실행중인 프로그램중에 어떤 것이 GC 대상에서 제외되는 영역에 data를 조금씩 쌓고 있다는 얘기입니다.
GC 대상에서 제외되는 영역은 다음과 같은 곳이 있습니다.
- Http Session에 넣는 데이터..(세션 타임아웃 또는 invilidate 씨 까지 제외됨)
- Static 변수
- 서블릿 또는 jsp의 멤버변수 ( WAS 기동 후 최초 호출 시 인스턴스 화 되어 WAS가 내려 갈 때 까지 사라지지 않음 )
- EJB의 멤버변수( pool안에서 객체가 존재하는 한 GC대상에서 제외)
위 같은 영역에 프로그램에서 data를 add 하는 구조를 메모리 릭 이라고 할 수 있습니다.
IBM 또는 SUN JDK 인 경우에는 heapdump를 통해서 분석하여 어느 데이터가 메모리를 많이 잡고 있는지를 알 수 있습니다.
Heapdump 사용법 및 분석 법은 다음을 참조 하시면 됩니다.
http://www.alphaworks.ibm.com/aw.nsf/FAQs/heaproots
http://www-1.ibm.com/support/docview.wss?uid=swg21190476
http://www.skywayradio.com/tech/WAS51/IBMHeapDump/
'java' 카테고리의 다른 글
VM 분석 : Chapter 1 Performance - 2 / -Xrunprof 옵션 (0) | 2008.09.17 |
---|---|
java.lang.OutOfMemoryError - OOME (0) | 2008.09.17 |
java의 excel 컨트롤 - jxl, poi (0) | 2008.09.16 |
jxl 을 이용하여 images 가져오기 (0) | 2008.09.16 |
SOA 기술자료 특집 - IBM developerworks (0) | 2008.09.10 |