JVM은 자바 애플리케이션 실행을 위한 가상 머신으로, 클래스 로더(Class Loader), 실행 엔진(Execution Engine), 메모리 영역(Memory Area), JNI(Java Native Interface)로 구성됩니다.
.class
파일을 읽어 메모리에 로드.Java 실행과정
Java 소스 파일 작성 (.java) -> Java 컴파일러 실행(.class 변환) -> JVM이 실행되며, 클래스 로더(Class Loader)가 클래스 로딩 -> JVM 실행 엔진 동작
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JVM!");
}
}
클래스 로딩
HelloWorld.class
를 메서드 영역(Method Area)에 로드. 메서드 호출과 스택 메모리 활용
main()
메서드가 호출되며 스택(Stack)에 메서드 호출 기록(Frame)이 생성됩니다. args
배열은 Heap에 생성되고, 메서드 내 지역 변수로 스택에 참조가 저장됩니다.Heap 메모리와 객체 생성
"Hello, JVM!"
문자열은 String Pool(Heap 내부의 Constant Pool)에 저장됩니다. System.out
객체는 이미 JVM 실행 시 Heap에 생성되어 있으며 이를 참조합니다.PC 레지스터와 명령어 실행
main()
메서드의 현재 실행 중인 명령어를 추적하며 System.out.println() 호출.네이티브 메서드 스택 활용
System.out.println()
은 내부적으로 네이티브 코드(JNI)를 호출하며, 네이티브 메서드 스택에서 실행됩니다.Garbage Collector와 종료
main()
메서드가 종료되면 스택 메모리의 Frame이 제거되고, 참조되지 않는 객체는 Garbage Collector에 의해 회수됩니다.클래스 로더(Class Loader)는 JVM이
.class
파일을 읽어 메모리에 로드하고, 실행 준비를 하는 역할을 합니다.
로드(Loading):
.class
파일을 읽어 메서드 영역(Method Area)에 클래스 정보를 저장합니다.링크(Linking):
초기화(Initialization):
Bootstrap Class Loader:
JAVA_HOME/lib
에 있는 기본 Java 클래스(예: rt.jar
)를 로드.Extension Class Loader:
JAVA_HOME/lib/ext
의 확장 클래스(JAR 파일 포함)를 로드.Application Class Loader:
classpath
에 지정된 사용자 정의 클래스와 애플리케이션 코드를 로드.ClassLoader
를 상속해 네트워크, 암호화된 파일 등 다양한 소스로 클래스 로딩 가능. JVM 메모리는 Method, Heap, Stack, PC Register, Native Method Stack으로 나뉩니다.
OutOfMemoryError: Metaspace
발생.String
리터럴이 상수 풀에 저장되어 동일한 리터럴을 참조할 때 메모리를 절약.내용:
세분화:
Eden
, Survivor
로 나뉩니다.특징:
OutOfMemoryError: Java Heap Space
발생.사용 예:
new
키워드를 사용해 생성된 객체는 힙에 저장됩니다.StackOverflowError
발생.int x
)는 Stack에 저장됩니다.StackOverflowError
발생 가능.예제 코드
public class Example { public static void main(String[] args) { int x = 10; int y = 20; int sum = x + y; System.out.println(sum); } }
컴파일 후 바이트코드 (
javap -c Example
)public static void main(java.lang.String[]); 0: bipush 10 // 명령어: 10을 스택에 푸시 2: istore_1 // 명령어: 스택에서 10을 꺼내어 x에 저장 3: bipush 20 // 명령어: 20을 스택에 푸시 5: istore_2 // 명령어: 스택에서 20을 꺼내어 y에 저장 6: iload_1 // 명령어: 변수 x의 값을 스택에 푸시 7: iload_2 // 명령어: 변수 y의 값을 스택에 푸시 8: iadd // 명령어: 스택에서 두 값을 꺼내어 더함 9: istore_3 // 명령어: 결과를 sum에 저장 10: getstatic #2 // 명령어: System.out을 스택에 푸시 13: iload_3 // 명령어: sum의 값을 스택에 푸시 14: invokevirtual #3 // 명령어: println 호출 17: return // 명령어: main() 종료
PC 레지스터의 역할
- PC 레지스터는 현재 실행 중인 명령어의 주소를 추적합니다.
- 위 바이트코드 실행 과정에서, PC 레지스터는 다음과 같이 업데이트됩니다:
bipush 10
실행 전: PC 레지스터는0
을 가리킴.bipush 10
실행 후: PC 레지스터가2
로 업데이트.istore_1
실행 후: PC 레지스터가3
으로 이동.- 이러한 과정을 반복하여, 명령어 실행 위치를 지속적으로 추적합니다.
StackOverflowError
또는 OutOfMemoryError
발생 가능.System.loadLibrary()
호출 시 해당 네이티브 코드는 이 스택에서 실행됩니다.GC(Garbage Collection)는 JVM(Java Virtual Machine)의 Heap 영역에서 더 이상 사용되지 않는 객체를 자동으로 정리(회수)하는 메모리 관리 기법입니다. GC 덕분에 Java 개발자는 메모리를 직접 해제할 필요 없이 객체를 생성하고 사용할 수 있습니다.
Heap 영역의 구조
GC의 동작
GC는 메모리 관리를 자동화하지만, 성능 저하를 유발할 수 있습니다.
자동으로 메모리 관리:
메모리 누수 방지:
플랫폼 독립적:
성능 저하 가능성:
예상치 못한 타이밍에 실행:
설정이 어려울 수 있음:
Stop-the-World는 GC 실행 중 JVM이 모든 애플리케이션 스레드를 일시 정지시키는 현상입니다.
GC는 객체의 참조를 추적하고, 사용되지 않는 객체를 안전하게 삭제해야 합니다.
객체 참조가 변경될 수 있는 환경에서는 메모리 관리를 신뢰할 수 없으므로, GC 수행 중에는 애플리케이션의 모든 작업을 멈춥니다. (메모리 안정성 보장)
GC의 성능 문제나 메모리 부족 문제를 사전에 파악하기 위해 모니터링이 필요합니다.
java -XX:+PrintGCDetails MyApplication
GC 모니터링
- GC 로깅 활성화
- JVM 옵션 추가:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xlog:gc*:file=gc.log
- 모니터링 도구 사용
- VisualVM, JConsole: 실시간 메모리 사용 및 GC 활동 확인.
- Eclipse MAT: 힙 덤프 분석.
- APM 도구: New Relic, Dynatrace로 GC 시각화 및 알림 설정.
- 코드로 GC 정보 확인
- JMX를 활용해 GC 이름, 실행 횟수, 실행 시간 확인:
ManagementFactory.getGarbageCollectorMXBeans();
- GC 로그 분석 도구
- GCViewer, Garbage Cat로 GC 로그를 시각화 및 분석.
- 문제 해결 및 튜닝
- High Pause Time: G1 GC, ZGC 사용.
- Frequent GC: 힙 크기 조정, 객체 수명 최적화.
Out of Memory 에러는 힙 크기 조정, 메모리 누수 점검, GC 정책 변경으로 해결 가능합니다.
JVM 실행 시 -Xms
(초기 힙 크기)와 -Xmx
(최대 힙 크기) 옵션을 설정합니다.
java -Xms512m -Xmx2048m MyApplication
-Xlog:gc*
또는 -verbose:gc
로 메모리 사용량 확인.메모리 누수는 더 이상 참조되지 않는 객체가 해제되지 않아 메모리가 부족해지는 현상입니다. 이를 탐지하려면 도구를 사용해 힙 덤프를 분석합니다.
힙 덤프 생성
JVM 옵션으로 애플리케이션 충돌 시 힙 덤프를 자동으로 생성합니다:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump
힙 덤프란?
JVM의 Heap 메모리 상태를 스냅샷으로 저장한 파일.
객체 인스턴스와 참조 관계를 확인할 수 있어 메모리 누수 분석에 유용.
Eclipse MAT로 분석
VisualVM 사용
코드 점검 및 수정:
List
, Map
)의 객체를 명시적으로 제거.WeakHashMap
사용하여 참조가 끊어진 객체를 자동으로 GC.객체 수명 관리:
null
로 설정.객체의 생명주기란 객체가 생성되어 사용되고 더 이상 필요 없어질 때까지의 전체 과정을 의미합니다.
생성(메모리 할당) -> 사용 -> 비활성화 -> 소멸(GC가 처리)
GC 알고리즘 조정:
JVM이 메모리를 관리하는 방식으로, 애플리케이션 성격에 맞게 선택해야 합니다.
기본 정책
G1GC로 전환
-XX:+UseG1GC
다른 GC 정책
-XX:+UseConcMarkSweepGC
-XX:+UseZGC
GC 정책 선택 기준
G1GC 주요 특징
- Region 기반 메모리 관리
- 힙을 작은 Region으로 나누어 필요한 부분만 GC 수행.
- Pause Time 목표 설정
-XX:MaxGCPauseMillis=<N>
옵션으로 GC 지연 시간을 제어.- Concurrent Marking
- Old Generation 객체를 비차단 방식으로 추적, Full GC 빈도 감소.
- Garbage-First 알고리즘
- 가비지가 많은 Region부터 우선 청소하여 효율 극대화.
- Full GC 완화
- Mixed GC로 대부분의 Full GC를 대체, STW 시간을 최소화.
이 특징 덕분에 많은 메모리 할당/해제 작업과 낮은 지연 시간 요구 환경에서 G1GC가 효과적입니다.