1. 바이트코드(.class 파일) 실행
- 자바 코드는 .java 파일에서 작성됨
- 컴파일러(javac)가 이를 바이트코드(.class)로 변환
- JVM이 바이트코드를 실행하여 프로그램 동작
2. 플랫폼 독립성 제공
- 한 번 작성하면 어디서든 실행 가능 (Write Once, Run Anywhere)
- 운영체제(OS)와 관계없이 바이트코드를 실행할 수 있도록 환경을 제공
3. 메모리 관리 (Garbage Collection)
- 객체를 자동으로 관리하고, 필요 없는 객체를 삭제하여 메모리를 정리함
- Java 코드 작성 (
.java파일)- 컴파일 (
javac) → 바이트코드 (.class 파일) 생성- JVM 실행 → 클래스 로더가
.class파일 로드- 실행 엔진이 바이트코드를 해석하고 실행
- 필요할 경우 JIT 컴파일러가 최적화
- 객체 사용이 끝나면 GC가 메모리 정리
- 프로그램 종료
1. 부트스트랩 클래스 로더 (Bootstrap ClassLoader)
rt.jar 같은 Java의 핵심 클래스 (java.lang, java.util)를 로드System.out.println(String.class.getClassLoader()); // 출력: null (Bootstrap ClassLoader)
👉 String.class는 부트스트랩 클래스 로더가 로드했기 때문에 null 반환
2. 확장 클래스 로더 (Extension ClassLoader)
lib/ext 디렉터리에 있는 확장 클래스(javax.crypto, javax.sound 등)를 로드sun.misc.Launcher$ExtClassLoader로 구현됨System.out.println(com.sun.crypto.provider.AESKeyGenerator.class.getClassLoader());
// 출력: sun.misc.Launcher$ExtClassLoader@xxxxxx
👉 AESKeyGenerator는 확장 클래스 로더가 로드했기 때문에 출력
3. 애플리케이션 클래스 로더 (Application ClassLoader)
classpath 경로에 있는 클래스)를 로드java -cp 또는 CLASSPATH 환경 변수에서 지정한 클래스 로드sun.misc.Launcher$AppClassLoader로 구현됨System.out.println(ClassLoaderExample.class.getClassLoader());
// 출력: sun.misc.Launcher$AppClassLoader@xxxxxx
👉 우리가 만든 클래스는 애플리케이션 클래스 로더가 로드
로딩 (Loading), 링크 (Linking), 초기화 (Initialization) 3단계를 거쳐 이루어진다.
로딩 (Loading) 예제
class A {
static int value = 10;
static {
System.out.println("클래스 A 로딩됨");
}
}
public class Test {
public static void main(String[] args) {
A obj; // 여기서는 아직 클래스 로딩 X
obj = new A(); // 여기서 클래스 로딩됨!
}
}
// 출력: 클래스 A 로딩됨
👉 obj = new A(); 실행될 때 클래스 로딩 발생!
초기화 (Initialization) 예제
class B {
static int num = 100;
static {
System.out.println("클래스 B 초기화됨");
}
}
public class Test {
public static void main(String[] args) {
System.out.println(B.num); // 클래스 B가 여기서 초기화됨!
}
}
// 출력: 클래스 B 초기화됨
// 100
👉 B.num을 호출하는 순간 클래스가 초기화됨!
JVM 메모리는 메서드(Method) 영역, 힙(Heap) 영역, 스택(Stack) 영역,
PC 레지스터(PC Register), 네이티브 메서드 스택(Native Stack)으로 총 5가지 주요 영역으로 나뉘어 있다.
메서드(Method) 영역 예제
class Example {
static int staticVar = 100; // 메서드 영역에 저장됨
}
✔ staticVar 변수는 메서드 영역(Method Area)에 저장됨!
✔ 모든 인스턴스가 공유하는 영역
힙(Heap) 영역 예제
class Person {
String name; // 힙 영역에 저장됨
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person(); // 힙에 저장됨
Person p2 = new Person(); // 또 다른 객체 생성됨
}
}
✔ p1, p2는 힙 영역에 각각 저장됨
스택(Stack) 영역 예제
public class Test {
public static void main(String[] args) {
int x = 10; // 스택 영역에 저장됨
add(x); // add() 메서드 호출 → 새로운 스택 프레임 생성
}
public static void add(int num) {
int result = num + 5; // 스택 영역에 저장됨
}
}
✔ x, num, result → 스택 영역에 저장됨
✔ add()가 실행되면 새로운 스택 프레임(Stack Frame) 생성
✔ add() 실행이 끝나면 스택 프레임 제거됨
Person p = new Person(); // GC가 알아서 정리!
p = null; // 더 이상 참조되지 않으면 GC 대상이 됨
}
}
✔ 개발자가 직접 메모리를 해제할 필요가 없음!
void noMemoryLeak() {
Integer num = new Integer(10); // GC가 필요할 때 자동 해제!
}
✔ Java에서는 GC가 자동으로 불필요한 객체를 정리해서 메모리 누수 걱정 없음!
Integer num = new Integer(5);
num = null; // GC가 자동 정리 → Dangling Pointer 문제 없음!
✔ Dangling Pointer 문제 방지 → 안정적인 코드 작성 가능!
JVM이 OS에 독립적으로 메모리 관리
GC 최적화를 통한 성능 개선 (최신 GC 알고리즘 지원)
G1 GC, ZGC, Shenandoah GC 등 최신 GC는 성능 최적화 가능
G1 GC(Garbage First GC)
- 대용량 힙 메모리를 가진 애플리케이션에 최적화
- 중단 시간을 최소화하여 서버 성능 향상
ZGC (JDK 11~)
- 초저지연(10ms 이하) GC
- 대규모 시스템에서도 빠르게 동작