-
Notifications
You must be signed in to change notification settings - Fork 0
고맙다 JVM, 사과해라 JVM 크래시
http://d2.naver.com/helloworld/1134732
많은 개발자가 버그 원인을 인프라 탓으로 돌리곤 합니다. 하지만 거의 대부분, 아마도 99.9%는, 인프라의 잘못이 아닙니다. 그러나 JVM의 실행이 멈췄다는 것은 0.1%라고 무시할 수 없는 사례입니다.
JVM은 마치 자동차의 엔진과 같은 것이라 인위적으로 시동을 끄지 않는 이상(System.exit 등) 외부적인 요인으로 실행이 멈추면 안 됩니다. JVM이 멈춘다는 것은 단순히 특정 시간의 장애로만 끝나는 것이 아닙니다. 근본 해결이 없다면 상습적인 장애로 연결돼 불안정한 서비스라 낙인이 찍히고 사용자는 이 불안정한 서비스를 외면할 것입니다.
구성 항목 | 설명 |
---|---|
Header | 오류에 관한 간략한 정보가 기술된 영역. 프로세스 아이디, 스레드 아이디, VM버전, 크래시를 일으킨 function frame정보 등이 있다. |
Thread | 크래시 발생 당시의 스레드 관련 정보 |
Process | 모든 프로세스에 대한 정보. 이 프로세스에는 스레드 목록과 메모리 사용량에 대한 정보가 있다. |
System | 운영체제 버전, CPU정보, 메모리 설정 관련 정보 등 |
제일 먼저 Thread 섹션에서 JVM 크래시 순간의 스레드가 무엇인지를 확인해야 한다.
#
# A fatal error has been detected by the Java Runtime Environment:
# SIGSEGV (0xb) at pc=0xb7961e23, pid=11757, tid=1405516688
| | | | +--- thread id
| | | +------------- process id
| | +--------------------------- program counter
| | (instruction pointer)
| +--------------------------------------- signal number
+---------------------------------------------- signal name
#
# JRE version: 6.0_18-b07
# Java VM: Java HotSpot(TM) Server VM (16.0-b13 mixed mode linux-x86 )
# Problematic frame:
# V [libjvm.so+0x5a2e23]
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
--------------- T H R E A D ---------------
Current thread (0x09a57400): GCTaskThread [stack: 0x53be7000,0x53c68000] [id=11760]
- JavaThread
- VMThread
- CompilerThread
- GCTaskThread
- WatcherThread
- ConcurrentMarkSweepThread
제일 먼저 확인해야 할 부분은 Thread 섹션에서 JVM 크래시 순간의 스레드가 무엇인지다. 위와 같이 가비지 컬렉션(garbage collection) 스레드의 특수한 경우라면 가비지 컬렉션 자체에 문제가 있지 않나 의문을 가질 수 있으나 판단을 내리기에는 정보가 부족하다.
Current thread (0x0805ac88): JavaThread "main" [_thread_in_native, id=21139]
| | | | +-- ID
| | | +------------- state
| | +-------------------------- name
| +------------------------------------ type
+-------------------------------------------------- pointer
스레드 상태 | 설명 |
---|---|
_thread_uninitialized |
Thread is not created. This occurs only in the case of memory corruption. |
_thread_new |
Thread has been created but it has not yet started. |
_thread_in_native |
Thread is running native code. The error is probably a bug in native code. |
_thread_in_vm |
Thread is running VM code. |
_thread_in_Java |
Thread is running either interpreted or compiled Java code. |
_thread_blocked |
Thread is blocked. |
..._trans |
If any of the above states is followed by the string _trans , that means that the thread is changing to a different state. |
http://www.oracle.com/technetwork/java/javase/felog-138657.html#gbwcy
Heap
PSYoungGen total 282176K, used 275243K [0x9ecf0000, 0xb4240000, 0xb4240000)
eden space 214336K, 100% used [0x9ecf0000,0xabe40000,0xabe40000)
from space 67840K, 89% used [0xb0000000,0xb3b7ad00,0xb4240000)
to space 67328K, 43% used [0xabe40000,0xadae6ba0,0xb0000000)
PSOldGen total 699072K, used 299948K [0x74240000, 0x9ecf0000, 0x9ecf0000)
object space 699072K, 42% used [0x74240000,0x8672b218,0x9ecf0000)
PSPermGen total 524288K, used 42358K [0x54240000, 0x74240000, 0x74240000)
object space 524288K, 8% used [0x54240000,0x56b9d808,0x74240000)
Java의 힙 메모리 영역 중 Eden 영역(eden space)이 100%라는 정보를 볼 수 있다. Oracle JVM 에서는 새로 생성됐거나 생성된 지 얼마 되지 않은 객체는 Eden 영역에 저장된다. Eden 영역이 일정 비율 이상 차면 설정에 따라 가비지 컬렉션이 발생하고, 계속 생성돼 있을 필요가 있는 객체는 S0(from)라는 Survivor 영역으로 옮겨진다. 다시 Eden 영역이 차면 S1(to)이라는 또 다른 Survivor 영역으로 이동한다. 설정에 따라 조금씩 동작 방법이 다르지만 좀 더 오래 생성돼 있어야 하는 객체는 가비지 컬렉션을 거치면서 Old 영역으로 이동한다.
S0(from)와 S1(to) 중 하나의 영역은 반드시 비어 있어야 하는데 크래시 로그에서 메모리 영역의 from 영역과 to 영역은 각각 89%와 43%다. 즉 가비지 컬렉션이 기대대로 동작하지 않은 것이다.
정석대로라면 다음에 할 작업은 추측한 시나리오를 바탕으로 JVM 버전의 버그 정보를 조회하는 것이다.
문제가 발생한 JVM 버전은 6.0_18(b07) 버전이었다(6.0_18(b07) 버전 버그 리포트 참고). 이 버그와 관련된 버그가 JVM 7.0-b74에서 발생했으며, 자세한 버그의 내용은 JVM 7.0-b74의 버그 리포트에서 확인할 수 있었다
여기까지 왔다면 개발자의 성향에 따라 2가지 방법 중 하나를 선택한다. 버그 리포트에서 설명하는 JVM 옵션을 추가해 사용하거나, 안정된 JVM 버전으로 다운그레이드하는 것이다. 결국 신규 서버를 기존 서버가 사용하는 6.0_14 버전으로 다운그레이드하고 성능 테스트를 다시 실행했더니 가상 사용자 200에서 시스템의 작동이 중단되거나 중지되는 현상 없이 10여 분 동안 서비스가 지속됐다. 추측도 어느 정도 맞았고 성능 테스트도 해결돼 모든 게 잘 완료됐다고 볼 수 있지만 추가로 더 확인할 것이 있다.
추가로 확인할 것은 릴리스 노트다. 6.0_18 버전의 정식 릴리스 노트에서 관련 버그가 소개됐는지 확인하고 6.0_14 버전에도 문제가 없는지 확인하는 것이다. 6.0_18 버전의 릴리스 노트에 관련 내용이 있었다.
HTTP 완벽 가이드
Learning HTTP/2
개발자가 반드시 정복해야할 객체지향과 디자인 패턴