JVM 기술면접 Q&A 20선
1. JVM의 클래스 로딩 과정을 설명해주세요.
A. 클래스 로딩은 3단계입니다.
- Loading:
.class 파일을 읽어 메모리에 올림. Bootstrap → Extension → Application ClassLoader 순으로 위임(Delegation).
- Linking:
- Verification: 바이트코드 유효성 검증
- Preparation: static 변수 기본값 할당
- Resolution: 심볼릭 참조를 실제 메모리 참조로 변환
- Initialization: static 초기화 블록 실행, static 변수에 실제 값 할당.
2. ClassLoader의 계층 구조와 위임 모델을 설명해주세요.
A.
Bootstrap ClassLoader (JVM 내장, C++로 구현)
└── Extension ClassLoader (jre/lib/ext)
└── Application ClassLoader (classpath)
Parent Delegation Model: 클래스 로드 요청이 오면 자신이 먼저 처리하지 않고 부모 ClassLoader에 위임. 부모가 못 찾을 때만 자신이 로드. → java.lang.String 같은 핵심 클래스를 사용자가 임의로 바꾸는 것을 방지.
3. JIT(Just-In-Time) 컴파일러란?
A. JVM은 처음에 바이트코드를 인터프리터로 실행합니다. JIT 컴파일러는 자주 실행되는 코드(Hot Spot)를 감지해 네이티브 코드로 컴파일하여 캐시합니다.
- C1 컴파일러: 빠른 컴파일, 적은 최적화 (클라이언트 모드)
- C2 컴파일러: 느린 컴파일, 강한 최적화 (서버 모드)
- Java 8+: Tiered Compilation으로 C1 → C2 단계적 적용.
4. GC 알고리즘 종류와 특징을 설명해주세요.
A.
- Serial GC: 단일 스레드. 소규모 애플리케이션용.
- Parallel GC (Java 8 기본): 멀티스레드 Minor GC. 처리량(Throughput) 중심.
- CMS (Concurrent Mark Sweep): GC 스레드와 앱 스레드 동시 실행. 낮은 지연시간. Java 14에서 제거.
- G1GC (Java 9+ 기본): Region 단위 관리. STW 예측 가능. 대용량 Heap에 적합.
- ZGC / Shenandoah: 초저지연. 대부분의 작업이 동시 실행. 수백 GB Heap에서도 ms 미만 STW.
5. Young Generation과 Old Generation의 차이는?
A.
- Young Generation: 새로 생성된 객체. Eden + Survivor0 + Survivor1으로 구성. Minor GC가 빠르게 처리.
- Old Generation: Young에서 오래 살아남은 객체(tenured). Major GC는 더 느리고 긴 STW.
- 객체 이동 흐름: Eden → (Minor GC 생존) → Survivor → (age 임계치 초과) → Old Generation.
-XX:MaxTenuringThreshold로 Old로 승격되는 나이 조정 가능.
6. STW(Stop-The-World)란 무엇이고 줄이는 방법은?
A. GC 수행을 위해 모든 애플리케이션 스레드를 일시 정지하는 현상. GC 루트에서 객체 그래프를 안전하게 탐색하기 위해 필요합니다.
- 줄이는 방법:
- G1GC/ZGC 등 저지연 GC 알고리즘 선택
- Heap 크기 적정하게 설정 (너무 크면 GC 시간 증가)
- 객체 생성 줄이기 (객체 풀링, 불변 객체 재사용)
-XX:MaxGCPauseMillis로 목표 STW 시간 설정
7. OutOfMemoryError의 종류와 원인은?
A.
- Java heap space: Heap 메모리 부족. 메모리 누수 또는 Heap 크기 부족.
- GC overhead limit exceeded: GC가 98% 시간을 사용하지만 2%도 회수 못할 때.
- Metaspace: 클래스 메타데이터 영역 부족. 동적 클래스 생성 과다(Reflection, 프록시).
- Unable to create new native thread: OS 스레드 한계 초과.
- Direct buffer memory: NIO DirectByteBuffer 과다 사용.
8. Java에서 스레드 동작 방식과 JVM 스레드 모델은?
A. Java 스레드는 OS 스레드(Kernel Thread)와 1:1로 매핑됩니다(Java 19 이전).
- 스레드 생성 비용이 높아 ThreadPool 사용이 필수.
- Java 19+ Virtual Thread (Project Loom): JVM이 관리하는 경량 스레드. OS 스레드 1개에 수천 개의 Virtual Thread 매핑. I/O 대기 시 OS 스레드를 블로킹하지 않고 다른 Virtual Thread 실행.
9. JVM 튜닝 시 주요 JVM 옵션을 설명해주세요.
A.
-Xms512m → 초기 Heap 크기
-Xmx2g → 최대 Heap 크기
-XX:+UseG1GC → G1GC 사용
-XX:MaxGCPauseMillis=200 → STW 목표 시간
-XX:MetaspaceSize=256m → Metaspace 초기 크기
-XX:+HeapDumpOnOutOfMemoryError → OOM 시 Heap Dump
-verbose:gc → GC 로그 출력
-Xms와 -Xmx를 동일하게 설정하면 Heap 재할당 오버헤드 제거.
A.
- PermGen (Java 7 이하): JVM Heap 내 고정 크기 영역. 클래스 메타데이터 저장. 크기 초과 시
OutOfMemoryError: PermGen space.
- Metaspace (Java 8+): Native 메모리(OS 메모리) 사용. 기본적으로 자동 확장. 클래스 메타데이터를 Heap 밖으로 분리해 PermGen OOM 문제 해결.
-XX:MaxMetaspaceSize로 최대치 설정 가능.
11. Heap Dump와 Thread Dump의 차이는?
A.
- Heap Dump: 특정 시점의 Heap 메모리 전체 스냅샷. 객체 수, 점유 크기, 참조 관계 포함. OOM 분석에 사용.
jmap -dump 또는 -XX:+HeapDumpOnOutOfMemoryError.
- Thread Dump: 모든 스레드의 현재 스택 상태 스냅샷. 데드락, 스레드 블로킹 분석에 사용.
jstack <pid> 또는 kill -3 <pid>.
12. 데드락(Deadlock)의 발생 조건과 감지 방법은?
A. 데드락의 4가지 필요 조건(Coffman 조건):
- 상호 배제: 자원을 하나의 스레드만 점유
- 점유와 대기: 자원을 가진 채 다른 자원 대기
- 비선점: 다른 스레드의 자원 강제 해제 불가
- 순환 대기: 스레드 간 원형으로 자원 대기
감지: Thread Dump에서 BLOCKED 상태의 스레드와 순환 참조 확인. JConsole/VisualVM의 Detect Deadlock 기능 활용.
13. Java의 동시성 컬렉션(Concurrent Collection)이란?
A. 멀티스레드 환경에서 안전하게 사용 가능한 컬렉션.
ConcurrentHashMap: 버킷 단위 락. Collections.synchronizedMap()보다 성능 우수.
CopyOnWriteArrayList: 쓰기 시 배열 복사. 읽기가 압도적으로 많을 때 유리.
BlockingQueue (LinkedBlockingQueue, ArrayBlockingQueue): 생산자-소비자 패턴에 활용.
ConcurrentLinkedQueue: lock-free 큐.
14. JVM의 인라이닝(Inlining) 최적화란?
A. JIT 컴파일러가 메서드 호출을 호출 코드 안으로 직접 삽입하는 최적화.
- 메서드 호출 오버헤드 제거 (스택 프레임 생성/해제 비용 절감)
- 추가 최적화(상수 폴딩, 데드 코드 제거) 가능
final 메서드, private 메서드, 작은 메서드에 잘 적용됨
-XX:MaxInlineSize로 인라이닝 크기 임계값 조정 가능
15. GC 로그에서 확인해야 할 지표는?
A.
- GC 빈도: Minor GC가 너무 자주 → Eden 크기 조정 필요
- STW 시간: Major GC의 STW가 길면 G1/ZGC 전환 고려
- Heap 사용률: GC 후에도 Heap이 계속 높으면 메모리 누수 의심
- Promotion Failure: Young → Old 승격 실패. Old가 가득 차면 발생
- Concurrent Mode Failure (CMS): 동시 GC 진행 중 Old가 가득 찬 경우
16. 바이트코드(Bytecode)란 무엇인가요?
A. Java 소스코드를 javac로 컴파일한 플랫폼 독립적인 중간 코드. .class 파일에 저장되며 JVM이 해석/실행합니다.
javap -c ClassName으로 바이트코드 확인 가능
iload, istore, invokevirtual 등 JVM 명령어로 구성
- WORA(Write Once Run Anywhere)의 핵심: 바이트코드는 어느 OS의 JVM에서든 동일하게 실행
17. Escape Analysis란?
A. JIT 컴파일러가 객체의 참조 범위를 분석해 Heap 대신 Stack에 할당할 수 있는지 판단하는 최적화.
- 객체가 메서드 밖으로 탈출(Escape)하지 않으면 Stack 할당 → GC 부담 감소
- Stack Allocation: Escape 없는 객체를 Stack에 할당
- Scalar Replacement: 객체를 분해해 필드를 레지스터/Stack 변수로 처리
-XX:+DoEscapeAnalysis (기본값 활성화)
18. Java에서 finalize() 메서드의 문제점은?
A. finalize()는 GC가 객체를 수거하기 전에 호출되는 메서드지만 심각한 문제가 있습니다.
- 호출 보장 없음: JVM이 finalize()를 언제, 또는 아예 호출하지 않을 수 있음
- 성능 저하: finalize()가 있는 객체는 GC가 2번 처리 (Finalization Queue)
- 부활 가능성: finalize()에서 this 참조를 저장하면 객체가 GC 대상에서 제외
- Java 9+에서
@Deprecated. try-with-resources와 Cleaner로 대체.
19. AOT(Ahead-Of-Time) 컴파일과 JIT의 차이는?
A.
- JIT: 실행 중에 Hot Spot을 감지해 컴파일. 워밍업 시간 필요. 런타임 정보로 최적화 우수.
- AOT: 실행 전에 미리 네이티브 코드로 컴파일. 워밍업 없음. 즉시 최고 성능.
- GraalVM Native Image: AOT 컴파일로 JVM 없이 실행 가능한 바이너리 생성. 시작 시간 수십 ms, 메모리 사용량 대폭 감소. 컨테이너/서버리스 환경에 유리.
20. JVM 모니터링 도구에는 어떤 것들이 있나요?
A.
- jps: 실행 중인 JVM 프로세스 목록
- jstat: GC, 클래스 로딩, JIT 컴파일 통계 실시간 확인.
jstat -gcutil <pid> 1000
- jmap: Heap Dump 생성, Heap 요약 정보
- jstack: Thread Dump 출력
- jconsole / VisualVM: GUI 기반 종합 모니터링
- APM (Pinpoint, Datadog, New Relic): 실무에서 분산 추적 + JVM 지표 통합 모니터링