문제 정의
질문 원문: How do you crash a JVM? 출처: StackOverflow 65200
면접 질문이나 테스트 시나리오에서 “JVM을 크래시시키는 방법"을 물어볼 때가 있습니다. 원문 작성자는 “무한 for 루프로 메모리를 모두 소진하면 되지 않을까"라고 생각했지만, 답은 그렇게 단순하지 않습니다.
먼저 핵심 구분을 명확히 해야 합니다. Stack Overflow에서 가장 많은 표를 받은 답변(181표)은 다음과 같이 지적합니다:
- OutOfMemoryError나 StackOverflowError를 던지는 것은 “크래시"가 아니다. 이것들은 일반 예외이며, JVM이 정상적으로 처리하는 자바 예외입니다.
- JVM을 진짜 크래시시키는 방법은 세 가지로 나뉩니다:
- JNI를 쓰고 네이티브 코드에서 크래시
- 보안 매니저가 없을 때 리플렉션으로 내부 필드를 조작해 크래시
- 특정 JVM 버전의 버그를 트리거
즉, “무한 루프로 메모리 고갈"은 예외를 유발할 뿐 진짜 크래시(hs_err 파일을 남기는 VM 죽음) 가 아닙니다.
원인 탐구
증상 fingerprint
목적을 모르고 “크래시"라고 부르면 혼란이 옵니다. 아래 표로 증상을 먼저 분류하십시오.
| 목표한 결과 | 실제 발생하는 것 | 예외인가, 크래시인가 | 흔한 부산물 |
|---|---|---|---|
| 깔끔한 종료 | 프로세스가 정상 종료 | 종료(System.exit) | 없음 |
| 메모리 소진 | OutOfMemoryError: Java heap space | 일반 예외 | 예외 스택 트레이스 |
| GC 압박 | GC overhead limit exceeded | 일반 예외 | 예외 스택 트레이스 |
| GC 무한 재귀 | JVM 네이티브 크래시(일부 구버전) | 진짜 크래시 | hs_err_pid*.log 또는 “Segmentation fault” |
| 네이티브 손상 | SIGSEGV | 진짜 크래시 | hs_err_pid*.log |
출처에 있는 내용입니다: ralfs 답변에서 재귀 객체 배열은 구버전(Java 5/6, OpenJDK 6)에서 hs_err 파일 없이 “Segmentation fault!“만 남기는 실제 크래시를 일으켰고, Java 7.0_09에서 부터는 일반 OutOfMemoryError로 바뀌었는 것을 댓글들을 통해 확인할 수 있습니다. 버전에 따라 결과가 완전히 달라지는 대표 예입니다.
먼저 확인할 것
크래시를 하기 전에 스스로에게 3-가지를 물으세요:
- 목표가 뭔가? — 자바 프로세스를 종료시키고 싶은 것인가, 아니면 실제 네이티브 크래시(hs_err 파일) 를 만들고 싶은 것인가?
- JVM 버전이 뭐냐? — 같은 코드도 Java 6에선 크래시, Java 8 이상에선 예외(또는 “GC overhead limit exceeded”)로 바뀝니다.
- 보안 매니저가 설치되어 있나? —
System.exit()과 리플렉션은 보안 매니저 앞에서는 동작하지 않을 수 있습니다.
근본 원인 분석
원인별 분기표 (Decision Tree)
JVM을 크래시시키는 시나리오를 판단 기준별로 나눈 표입니다. 기본 판단 기준: “예외가 아니라 진짜 프로세스 죽임"이 목표라면 종료·리소스·예외만으로는 부족합니다.
| 시나리오 | 목표 | 적용 지시자 | 예상 결과 | 권장 여부 |
|---|---|---|---|---|
| 애플리케이션 종료 | 깔끔한 종료 | System.exit(status) | 정상 종료 코드 | 재현·테스트에 유용 |
| 예외를 재현 | 예외 데이터 수집 | -Xmx 낮춤 + 메모리 소진 | OutOfMemoryError | 일반 시나리오 |
| 실제 크래시 재현 | hs_err 수집 | Unsafe.putAddress(0,0) | SIGSEGV + hs_err | 고급/위험 |
| JNI 네이티브 손상 | 네이티브 크래시 | .so/.dll 안에서 잘못된 C | SIGSEGV | 위험 |
| JVM 버그 트리거 | 특정 버전 크래시 | 버전별 결함 악용 | 버전에 따라 다름 | 비결정적 |
판단 조건
- “그냥 죽이는” 것이 목표라면 →
System.exit()가 가장 단순하고 결정적인 방법입니다. - 실제 크래시(hs_err) 파일이 필요하다면 →
Unsafe또는 JNI, 또는 특정 JVM 버그를 트리거해야 합니다. - 예외(OutOfMemoryError 등) 재현이 목표라면 → 메모리 소진이나 무한 재귀로 충분하지만 이는 “크래시"가 아닙니다.
코드 해결책
상황 1 — 깔끔한 종료가 목표라면: System.exit()
원본 답변에서 가장 결정적인 단일 방법은 System.exit() 입니다. 이는 정리(cleanup) 없이 JVM을 즉시 종료합니다. 보안 매니저가 설치되어 있다면 SecurityException이 발생할 수 있습니다.
| |
2 — 실제 네이티브 크래시를 만들고 싶다면 (sun.misc.Unsafe)
표를 가장 많이 받은 코드 예시(Dave Griffiths 답변)입니다. Unsafe.putAddress(0, 0)은 널 주소에 쓰기 때문에 SIGSEGV를 일으키고 hs_err_pid*.log를 생성합니다. JDK 8u131(Linux x64)에서 SIGSEGV와 hs_err_pid*.log 생성이 확인되었다는 댓글이 있습니다.
| |
클래스가 부트 클래스패스에 없거나 Unsafe 접근 제한이 있는 환경에서는 -Xbootclasspath/p:. 옵션을 활용할 수도 있습니다(원본 답변 기준, JDK 9 이후에는 부트 클래스패스 옵션이 제거됨).
3 — JNI에서 네이티브 코드로 크래시
JNI로 C/C++ 네이티브 메서드를 호출하고 그 안에서 잘못된 포인터 연산을 하면 JVM는 막지 못하고 크래시합니다. 원본 답변(Dan Dyer, 130표)은 “JNI에서는 크래시가 기본값"이라고 표현합니다.
| |
4. JVM 버그를 트리거하는 방법(버전 한정)
특정 구버전(HotSpot)에서는 아래 재귀 코드가 GC에서 native 스택 오버플로를 일으켜 진짜 크래시를 내었습니다 (Java 5/6, OpenJDK 6 등). 다만 Java 7.0_09에서는 더 이상 아닙니다.
| |
- Java 5/6/OpenJDK 6: “Segmentation fault!” (일부는 hs_err 파일 없이)
- Java 7.0_09:
OutOfMemoryError: Java heap space - JDK 8 u71:
java.lang.OutOfMemoryError: GC overhead limit exceeded
이 하나의 코드가 버전에 따라 완전히 다른 결과가 나타나는 것이 **“반드시 빙판 1개만 보고 판단하지 말라”**는 교훈을 보여줍니다.
검증 명령 ★
해결 후 크래시가 맞는지 확인하는 방법
해당 방법으로 온전히 크래시(또는 종료)가 되었는지 확인하는 명령들입니다.
| |
hs_err 파일을 의도적으로 생성하고 싶을 때는
Unsafe.putAddress(0,0)가 가장 재현성이 높은 선택입니다(원문 댓글에서 클라우드 인프라의 로그 수집 파이프라인 테스트에 실제 활용했다는 제보가 있음). 이는 “일반적인 사용 시나리오"입니다.
실전 적용 체크
- 로컬 테스트: 작은 출력으로 크래시가 되는지,
hs_err_pid*.log가 같은 디렉터리에 생기는지 확인합니다. - CI/테스트 자원: 절대로
while(true)식 메모리 소진 코드를 CI에 그대로 넣지 마세요. 동료 프로세스에 메모리를 함께 소진시킬 수 있습니다(“환경에 따라 다를 수 있음”). - 프로덕션: 어떤 방식으로도 프로덕션 JVM을 의도적으로 크래시시키는 것은 부적절합니다. 크래시 테스트는 격리된 환경에서만 수행하십시오.
향후 예방 조치
- 목표와 미리 연결하라. “크래시"는 종료(System.exit), 예외(메모리)로 구분하는 등 정확한 용어를 쓰면 검색·디버깅이 빨라집니다.
- 버전과 빌드 명시 — 같은 코드가 Java 6에서는 진짜 크래시, Java 8에서는 예외인 이유 버전을 기록해 두어야 재현이 가능합니다.
- 보안 매니저 확인 —
System.exit/리플렉션이 제한되는 환경이면 다른 접근이 필요합니다. - hs_err 수집 파이프라인 구축 — 크래시 모니터링/수집을 테스트하고 싶다면 Unsafe 유형의 규모작은 발행만 사용하세요.
DevTrace verdict
“JVM 크래시"는 예외가 아니라 프로세스가 네이티브에서 죽는 것이다. 단순한 무한 루프는 예외만 만들므로, 진짜 크래시(그리고 hs_err 파일)를 만들고 싶다면 Unsafe 또는 JNI 네이티브 코드를 선택하고, 버전에 따라 결과가 달라지는 점을 반드시 감안해야 한다.
출처
- Stack Overflow: How do you crash a JVM?
- 인용 답변: rufus 181점 (3가지 크래시 방법 / 재귀 GC 예제), Dan 130점 (JNI), Dave 69점 (sun.misc.Unsafe)