JVM & GC

웃음인·2025년 4월 9일

Java

목록 보기
34/37
post-thumbnail

JVM(Java Virtual Machine) 구조

JVM(자바 가상 머신)은 Java 프로그램을 실행하기 위한 가상 머신이다
즉, 자바 코드를 실행할 수 있는 환경을 제공하는 소프트웨어이다 !

JVM은 크게 클래스 로더, 런타임 데이터 영역, 실행 엔진, 네이티브 인터페이스로 구성


클래스 로더 (ClassLoader)

  • .class 파일(바이트코드)을 JVM 메모리에 로딩하는 역할

  • 실행 시 필요한 클래스들을 로드하고, 링크하고, 초기화함

  • 3가지 주요 로더

    • Bootstrap ClassLoader (최상위, Java 기본 클래스 로드)
    • Extension ClassLoader (lib/ext에 있는 확장 클래스 로드)
    • Application ClassLoader (사용자가 작성한 클래스 로드)
  • 동작 과정

    • 로딩(Loading) → 클래스 파일을 읽어 메모리에 로드
    • 링크(Linking) → 클래스 내부 구조 분석 및 메모리 할당
    • 초기화(Initialization) → 정적 변수(static) 초기화

런타임 데이터 영역 (Runtime Data Area)

JVM이 프로그램 실행 중 데이터를 저장하는 공간으로, 여러 영역으로 나뉨

  • 메서드 영역 (Method Area)
    • 클래스 정보, 메서드 코드, static 변수 저장
    • 한 번만 로드되며 모든 스레드가 공유

  • 힙 영역 (Heap Area)
    • 객체와 인스턴스 변수 저장
    • GC (Garbage Collector)가 메모리 관리
    • 모든 스레드가 공유하는 영역

  • 스택 영역 (JVM Stack)
    • 각 스레드마다 생성되는 공간
    • 메서드 호출 시 지역 변수, 매개변수, 리턴 값 저장
    • 메서드가 끝나면 해당 스택 프레임 제거됨

  • PC 레지스터 (PC Register)
    • 현재 실행 중인 명령어의 주소를 저장하는 공간
    • 각 스레드마다 하나씩 존재

  • 네이티브 메서드 스택 (Native Stack)
    • Java가 아닌 네이티브 코드(C, C++ 등) 실행 시 사용

실행 엔진 (Execution Engine)
JVM이 바이트코드를 실제 기계어로 변환하고 실행하는 부분

  • 인터프리터 (Interpreter)
    • 바이트코드를 한 줄씩 읽고 실행
    • 실행 속도가 느림

  • JIT 컴파일러 (Just-In-Time Compiler)
    • 자주 실행되는 코드를 기계어로 변환하여 속도를 높임
    • 인터프리터보다 빠른 실행 속도 제공

  • 가비지 컬렉터 (Garbage Collector, GC)
    • 사용되지 않는 객체를 자동으로 제거하여 메모리를 관리

네이티브 인터페이스 (Native Interface)

  • JVM이 운영체제(OS)나 네이티브 코드(C, C++)와 상호작용할 수 있도록 도와줌

  • JNI(Java Native Interface)를 통해 네이티브 라이브러리를 호출할 수 있음


클래스 로더란?

클래스 로더(ClassLoader)는 JVM이 .class 파일(바이트코드)을
메모리에 로드하는 역할을 한다. 즉, Java 프로그램 실행 시 필요한 클래스를 찾아서
JVM의 메모리에 올리는 과정을 담당한다 !

👀 클래스 로더의 역할

   1. 클래스를 동적으로 로드(Loading)

  • Java 코드는 .class 파일로 변환됨 → 실행 시 JVM이 필요할 때 메모리에 적재
  • 미리 모든 클래스를 로드하는 것이 아니라, 필요할 때 로드 (동적 로딩)

   2. 클래스를 링크(Linking)

  • 메모리에 로드된 클래스를 사용할 수 있도록 검증, 준비, 해석 과정 수행

   3. 클래스를 초기화(Initialization)

  • static 변수 초기화 및 static {} 블록 실행

👀 클래스 로더의 종류
JVM에는 기본적으로 3가지 클래스 로더가 존재한다.
클래스를 부모 → 자식 순서로 로드하는
부트스트랩 방식 (Delegation Model, 위임 모델)을 사용한다 !

   1. 부트스트랩 클래스 로더 (Bootstrap ClassLoader)

   2. 확장 클래스 로더 (Extension ClassLoader)

   3. 애플리케이션 클래스 로더 (Application ClassLoader)

👀 클래스 로딩 과정 (3단계)

   1. 로딩 (Loading)

  • .class 파일을 찾아서 JVM의 메소드 영역(Method Area)에 저장
  • static 변수와 메서드 정보를 메모리에 올림

   2. 링크 (Linking)

  • 검증 (Verification): .class 파일이 올바른지 체크
  • 준비 (Preparation): static 변수 메모리 할당
  • 해석 (Resolution): 심볼릭 참조(메서드, 변수)를 실제 메모리 주소로 변환

   3. 초기화 (Initialization)

  • static 블록 실행 및 static 변수 초기화
  • 클래스 로더가 최종적으로 클래스 사용 가능 상태로 만듦

👀 클래스 로더 동작 방식 (Delegation Model)
클래스를 로드할 때, 부모 → 자식 순서로 요청을 위임하는 방식이다.

/* 클래스 로딩 요청 흐름 */

사용자 클래스 요청 → 애플리케이션 클래스 로더
                        │
                        ├── 부모(확장 클래스 로더)에게 요청
                        │      ├── 부모(부트스트랩 클래스 로더)에게 요청
                        │      │      ├── 요청한 클래스 찾음 → 로드
                        │      │      ├── 찾을 수 없음 → 자식에게 요청
                        │      ├── 요청한 클래스 찾음 → 로드
                        │      ├── 찾을 수 없음 → 자식에게 요청
                        ├── 요청한 클래스 찾음 → 로드
                        ├── 찾을 수 없음 → ClassNotFoundException 발생

부모가 먼저 클래스를 찾고, 없으면 자식이 찾음!
안전한 클래스 로딩을 위해 JVM이 사용하는 방식


JVM 메모리 구조(자세히 설명)

JVM(Java Virtual Machine) 메모리는 Java 프로그램 실행 중
데이터를 저장하는 공간이다. JVM 메모리는 여러 영역으로 나뉘어 관리되며,
효율적인 실행을 위해 역할별로 구분돼 있다.

JVM 메모리는 5가지 주요 영역으로 나뉜다.

  1. 메서드(Method) 영역 → 클래스 정보 & static 변수 저장

  2. 힙(Heap) 영역 → 객체(instance) 저장 (GC의 대상)

  3. 스택(Stack) 영역 → 메서드 실행 시 생성되는 지역 변수 저장

  4. PC 레지스터(PC Register) → 현재 실행 중인 명령어의 주소 저장

  5. 네이티브 메서드 스택(Native Stack) → Java가 아닌 네이티브 코드(C/C++) 실행 시 사용

1. 메서드(Method) 영역
메서드 영역은 JVM이 클래스 정보를 저장하는 공간이다.

  • 저장되는 데이터
    • 클래스 정보 (클래스, 인터페이스, 메서드, 필드 등)
    • static 변수 (클래스 변수)
    • 상수 풀 (Constant Pool) → final 상수 값 저장
    • 메서드 코드 (바이트코드)

2. 힙(Heap) 영역
힙 영역은 JVM이 객체(instance)를 저장하는 공간이다.
자바의 GC(Garbage Collector)가 관리하는 영역으로, 사용되지 않는 객체를
자동으로 제거한다 !

  • 저장되는 데이터
    • new 키워드로 생성한 객체
    • 인스턴스 변수(instance variable)
    • 클래스에서 생성된 배열 및 컬렉션(List, Map 등)

3. 스택(Stack) 영역
스택 영역은 메서드가 실행될 때 생성되는 지역 변수와 호출 정보를 저장하는 공간이다.
각 메서드 호출마다 스택 프레임(Stack Frame)이 생성되고, 메서드 실행이 끝나면 제거된다 !

  • 저장되는 데이터
    • 지역 변수 (Local Variables)
    • 메서드 호출 정보 (매개변수, 리턴 주소 등)
    • 메서드 실행 도중 생성된 임시 데이터

4. PC 레지스터 (PC Register)
PC 레지스터(Program Counter Register)는 현재 실행 중인 명령어의
메모리 주소를 저장하는 공간이다.
각 스레드마다 하나의 PC 레지스터를 가짐 (멀티스레드 환경에서 중요!)

✔ 메서드 실행 흐름을 관리하는 역할!

5. 네이티브 메서드 스택 (Native Method Stack)
네이티브 메서드 스택은 Java가 아닌 네이티브 코드(C, C++ 등)를 실행할 때 사용하는 공간이다.
Java 프로그램이 네이티브 메서드를 호출하면, 해당 메서드의 실행 정보를 관리한다 !

GC란?

GC(Garbage Collection, 가비지 컬렉션)은 JVM이 더 이상 사용되지 않는 객체를
자동으로 제거하여 메모리를 관리하는 기능이다. 즉, 힙(Heap) 영역에서 필요 없는
객체를 찾아 삭제해 메모리 누수를 방지하는 역할을 한다 !

👀 GC 동작 과정 (Mark & Sweep 알고리즘)
JVM의 GC는 객체를 정리하는 과정을 다음 2단계로 진행한다.

   1. 마킹(Marking)

  • 모든 객체를 검사하여 사용 중인 객체와 사용되지 않는 객체를 구분
  • 참조가 있는 객체는 "생존 객체", 참조가 없는 객체는 GC 대상으로 표시됨

   2. 삭제(Sweeping)

  • GC 대상 객체를 제거하고 메모리를 해제함.
  • Compact(압축) 작업을 수행하여 메모리를 연속적인 공간으로 정리할 수도 있음.

   GC 대상이란 ?
   ‣ 더 이상 참조되지 않는 객체
   ‣ 스택에서 사용되지 않는 객체
   ‣ 클래스 로더가 제거된 후 남아 있는 클래스 데이터


GC의 장단점

😄 GC의 장점

1. 메모리 자동 관리 (수동 해제 불필요)

  • 더 이상 참조되지 않는 객체를 자동으로 정리해 줌

2. 메모리 누수(Leak) 방지

  • Java의 GC는 사용되지 않는 객체를 자동으로 제거하여 메모리 누수를 방지
  • C++에서는 delete를 빼먹으면 메모리 누수가 발생하지만,
    Java는 이런 문제를 자동으로 해결

3. Dangling Pointer(잘못된 메모리 참조) 문제 방지

  • C++에서는 메모리를 해제한 후에도 포인터가 남아 있을 수 있음 (Dangling Pointer 문제)
  • Java에서는 GC가 알아서 정리해 주므로 Dangling Pointer 문제가 발생하지 않음

4. JVM이 OS에 독립적으로 메모리 관리

  • Java의 GC는 운영체제(OS)와 상관없이 동일한 방식으로 동작
  • C++에서는 OS별로 메모리 관리 방식이 다를 수 있지만, Java는 JVM이 통합적으로 관리

  → 한 번 개발하면 Windows, Linux, Mac에서 동일하게 실행 가능!

5. GC 최적화를 통한 성능 개선 (최신 GC 알고리즘 지원)

  • Java는 GC 성능을 개선하기 위해 다양한 GC 알고리즘을 제공
  • G1 GC, ZGC, Shenandoah GC 등 최신 GC는 성능 최적화 가능

😔 GC의 단점
1. GC 실행 시 성능 저하

  • GC가 실행될 때 JVM은 일시적으로 모든 애플리케이션 실행을 멈춤
    (Stop-the-World, STW)
  • GC가 실행되는 동안 다른 작업을 수행할 수 없으므로, 응답 속도가 느려질 수 있음
  • 특히 대용량 데이터 처리 및 실시간 애플리케이션(게임, 금융 시스템 등)에서
    치명적일 수 있음

  → 객체가 너무 많이 생성되면 GC가 자주 실행되고,
      STW로 인해 애플리케이션이 멈추는 시간이 증가

2. 높은 CPU 사용량 (GC Overhead)

  • GC는 불필요한 객체를 찾아 삭제하는 작업이므로 CPU를 많이 사용할 수 있음
  • GC가 자주 실행되면 CPU가 GC 작업에 집중하여 실제 프로그램 성능이 저하될 수 있음

3. 메모리 단편화 문제

  • GC가 객체를 제거해도 메모리 조각(Fragmentation)이 남을 수 있음
  • 메모리 단편화가 심해지면 힙(Heap) 영역이 충분해도 새로운 객체를 할당할 공간이
    부족할 수 있음

4. GC 실행 시 애플리케이션 제어 어려움

  • Java에서는 GC가 자동으로 실행되므로, 개발자가 GC 실행 시점을 정확히 제어할 수 없음
  • System.gc();를 호출하여 GC 실행 요청은 가능하지만, JVM이 반드시 실행하는 것은 아님

GC를 모니터링해야 하는 이유는?

1. GC 실행 시간 & Stop-the-World(STW) 발생 시간 확인

  • GC가 실행될 때 JVM은 Stop-the-World(STW) 상태가 되어 애플리케이션 실행이 멈춤
  • STW 시간이 길어지면 시스템 응답 속도가 저하됨
  • GC 실행 빈도가 너무 높으면 애플리케이션 성능이 떨어질 수 있음
    ex) 불필요한 객체 생성 → GC 과부하 발생 가능

2. 메모리 누수(Leak) 및 OutOfMemoryError(OOM) 감지

  • GC가 동작하더라도 객체가 계속 남아 있으면 메모리 누수가 발생할 수 있음
  • 특정 객체가 계속 참조되어 GC가 해제하지 못하면 메모리가 부족해지면서
    OOM(OutOfMemoryError) 발생

3. GC 알고리즘 선택 및 최적화

  • JVM은 여러 GC 알고리즘을 제공하지만,
    애플리케이션의 특성에 맞는 최적의 GC를 선택해야 함
  • 잘못된 GC 설정은 성능 저하를 초래할 수 있음

자바로 jvm 응용 프로그램을 개발한 후에 운영하다보면
out of memory가 나올 수 있는데 이 에러가 발생했을때 어떻게 대처해야 할까?

OutOfMemoryError는 JVM이 더 이상 사용할 수 있는 메모리가 없을 때 발생하는
오류이다. OOM이 발생하면 애플리케이션이 비정상적으로 종료될 수 있기 때문에
빠른 대응이 필요하다 !

⚠️ OOM 발생 유형 및 해결 방법

1. Java Heap Space 부족 (가장 흔한 OOM)

  • 원인

    • 애플리케이션이 너무 많은 객체를 생성하여 Heap 영역이 부족해짐
    • 메모리 누수(Leak)로 인해 GC가 객체를 해제하지 못함
  • 해결 방법

    • 1 ) JVM 힙 크기 증가
      Xmx 옵션을 설정하여 힙 메모리 크기를 늘릴 수 있음
      2 ) GC 튜닝 및 모니터링
      -  GC 로그를 분석하여 메모리 사용량을 확인하고, 적절한 GC 알고리즘 선택
    • 3) 메모리 누수 해결
      WeakReference 또는 SoftReference 활용하여 GC가 객체를 더 빨리 정리하도록 유도
      -  객체 참조를 null로 해제하여 GC가 제거할 수 있도록 함

2. GC Overhead Limit Exceeded (GC가 과부하)

  • 원인

    • GC가 너무 자주 실행되어 CPU 사용률이 높아지고 애플리케이션 성능 저하
    • Heap 크기는 충분하지만 객체가 너무 많아 GC가 너무 많은 시간을 사용함
  • 해결 방법

    • 1) JVM 힙 크기 증가 (-Xmx 옵션 설정)
    • 2) GC 튜닝 (G1 GC 또는 ZGC 사용)
    • 3) 객체 생성 최소화 및 재사용
      - String 객체를 반복적으로 생성하지 않도록 StringBuilder 사용
      - 객체 풀(Object Pooling) 기법을 사용하여 불필요한 객체 생성을 줄임

3. Direct ByteBuffer OOM (네이티브 메모리 부족)

  • 원인

    • ByteBuffer.allocateDirect()를 사용할 때 네이티브 메모리를 너무 많이 사용하면 발생
    • Netty, NIO 기반 애플리케이션에서 자주 발생
  • 해결 방법:

    • 1) Direct 메모리 크기 증가 (-XX:MaxDirectMemorySize)
    • 2) ByteBuffer 사용 후 clear() 호출

메모리 누수를 어떻게 확인해야 할까?

1. JVM 옵션을 활용한 GC 로그 분석

  • JVM 실행 시 GC 로그를 활성화하여 메모리 사용량을 추적할 수 있음!
/* GC 로그 예제 */

[GC (Allocation Failure)  512K->256K(1024K), 0.0056780 secs]
[GC (Allocation Failure)  768K->384K(1024K), 0.0034200 secs]

 → GC가 너무 자주 실행되고 힙(Heap) 메모리가 줄어들지 않는다면
    메모리 누수를 의심해야 함 !

2. JVisualVM을 활용한 Heap Dump 분석

  • JVisualVM(Java Visual VM)은 JVM의 메모리 상태를 실시간으로 모니터링할 수 있는 도구임 !

       JVisualVM 실행 → 실행 중인 Java 애플리케이션 선택
       → Heap Dump 생성 후 분석 (ProfilerHeap Dump)
       → 누수가 의심되는 객체 확인 (Instances 탭에서 계속 유지되는 객체 찾기)

3. Heap Dump 분석 (Eclipse MAT 사용)

  • Heap Dump를 저장한 후 Eclipse MAT(Memory Analyzer Tool)로 분석 가능
  • 누수가 발생하는 객체와 참조 경로를 시각적으로 분석

4. WeakReference 활용하여 누수 감지

  • 메모리 누수 가능성이 있는 객체를 WeakReference로 감싸면
    GC가 정상적으로 정리하는지 확인 가능

  → GC 후 null이면 정상적으로 해제됨, 그렇지 않으면 누수 가능성 있음!



GC에서 사용하는 알고리즘은 무엇이 있는지,
Java는 어떤 알고리즘을 사용하는지?

🪢 GC에서 사용하는 대표적인 알고리즘

  • GC는 기본적으로 Mark & Sweep 방식에 기반한 여러 알고리즘을 사용함
알고리즘설명장점단점
Mark-Sweep (마크 & 스윕)살아있는 객체를 마킹 후 삭제단순한 구현, 효율적메모리 단편화 발생 가능
Mark-Compact (마크 & 컴팩트)객체를 마킹 후 삭제 + 압축(Compaction)메모리 단편화 방지추가적인 연산 필요
Copying (복사 GC)객체를 두 개의 영역으로 나누고 한쪽으로 복사빠른 할당, 단편화 없음메모리 사용량 증가
Generational GC (세대별 GC)객체를 Young, Old 세대로 나누어 관리GC 성능 최적화GC 전략이 복잡함
Reference Counting (참조 카운팅)객체 참조 횟수를 카운팅하여 관리즉시 수거 가능순환 참조 문제 발생 가능

→ Java는 Mark-Sweep, Mark-Compact, Copying 방식을 조합한
    "세대별 GC(Generational GC)"를 사용함 !


🪢 Java의 GC 알고리즘 종류

  • Java는 JDK 버전에 따라 여러 가지 GC 알고리즘을 지원하며,
    애플리케이션 성능에 따라 적절한 GC를 선택할 수 있다.
GC 유형특징장점단점적합한 환경
Serial GC단일 스레드 GC구현이 단순, 메모리 사용량 적음STW 시간이 길어짐작은 애플리케이션
Parallel GC멀티 스레드 GC높은 처리량, 멀티코어 활용 가능STW 시간이 길 수 있음서버 애플리케이션
CMS GC동시 실행 GC응답 속도 향상메모리 단편화 발생 가능실시간 서비스
G1 GCRegion 기반 GCSTW 시간 최소화, 대용량 Heap 지원Serial GC보다 복잡대규모 시스템
ZGC초저지연 GCSTW 시간 10ms 이하, 초대형 Heap 지원JDK 11 이상에서만 사용 가능금융, 게임 서버
Shenandoah GC초고속 GC낮은 레이턴시, 빠른 GC 수행상대적으로 최신 기술실시간 애플리케이션

→ Java 8까지는 Parallel GC가 기본이었고, Java 9부터는 G1 GC가 기본 GC야!
→ JDK 11 이상에서는 ZGC, Shenandoah GC 같은 초저지연 GC를 선택할 수도 있어!


Java 8 기준으로 GC는 어떤 방식으로 수행될까?

Java 8에서는 기본 GC 알고리즘으로 Parallel GC(병렬 GC)를 사용함
또한 JVM은 세대별 GC(Generational GC) 방식을 적용해
Young Generation과 Old Generation을 다르게 관리함

Java 8의 기본 GC 알고리즘: Parallel GC

  • Young Generation은 Copying GC 방식으로 관리
  • Old Generation은 Mark-Sweep-Compact 방식으로 관리
  • 멀티코어 CPU를 활용하여 병렬로 GC 실행
  • roughput(처리량) 최적화에 초점

Java 8의 GC 구조: 세대별 GC(Generational GC)

  • Java 8의 Heap 메모리는 크게 Young Generation(Young Gen)과
    Old Generation(Old Gen)으로 나뉘며, 각각 다른 방식으로 GC를 수행함 !
/*  Java 8의 Heap 메모리 구조 */

┌───────────────────────────────────┐
│          Heap Memory           │
├────────────────────┬──────────────┤
│ Young Gen        │  Old Gen    │
│ (Eden + S0 + S1) │  (Tenured)  │
└────────────────────┴──────────────┘

→ 각 영역에서 GC가 수행되는 방식이 다름!

왜 Heap 영역은 Young Generation과 Old Generation으로 나뉠까?

Heap 메모리를 Young Generation과 Old Generation으로 나누는 이유는
GC 성능 최적화 때문이다 ! 이렇게 나누면 객체의 생존 기간을
고려한 GC 전략을 적용할 수 있어서 전체적인 성능이 향상
된다 !

1. 객체의 생존 기간이 다름
대부분의 객체는 생성된 후 금방 사라짐

  • 웹 요청 처리, 임시 데이터 저장, 컬렉션 정리 등의 작업에서
    짧은 시간 동안만 존재하는 객체가 많음.
  • 반면, 일부 객체는 오랫동안 유지됨
    ex) 애플리케이션의 설정, 사용자 세션, 싱글톤 객체

→ 객체의 생존 기간을 고려하여 GC를 최적화하기 위해
    Heap을 Young Gen과 Old Gen으로 나눔!


2. Young Generation과 Old Generation의 차이점

  • Young Generation

    • 새롭게 생성된 객체가 저장되는 공간
    • 대부분의 객체가 Young Gen에서 금방 사라짐 (GC가 자주 실행됨)
    • Young GC는 "Copying GC" 방식을 사용하여 빠르게 객체를 정리
  • Old Generation

    • Young Generation에서 살아남은 오래된 객체들이 저장되는 공간
    • 객체 수명이 길기 때문에 GC가 덜 자주 실행됨
    • Old GC는 "Mark-Sweep-Compact" 방식을 사용하여 메모리 단편화를 방지

→ Young GC는 자주 실행되지만 빠름, Old GC는 실행 빈도는 낮지만 오래 걸림!

3. GC 성능 최적화를 위해 세대별 GC 전략 사용

  • Young Generation GC (Minor GC)

    • Young Gen에서는 대부분의 객체가 금방 필요 없어지기 때문에,
      GC를 자주 실행하여 빠르게 제거

    • GC 방식: "Copying GC" → 살아남은 객체만
      Survivor 영역으로 이동 (빠른 수행 가능!)

    • Young GC 과정

    1. 새 객체는 Eden 영역에 할당됨
    2. Eden이 가득 차면 Minor GC 실행
    3. 살아남은 객체는 Survivor 영역(S0 → S1)으로 이동
    4. 여러 번 살아남은 객체는 Old Gen으로 이동

   → Young GC는 빠르게 수행되어 애플리케이션 성능에 큰 영향을 주지 않음!

  • Old Generation GC (Major/Full GC)
    • Old Gen은 오래 살아남은 객체가 저장되므로 GC 실행 빈도를 줄여야 함
    • GC 방식: "Mark-Sweep-Compact" → 메모리 단편화를 방지
    • Full GC는 Stop-the-World(STW)가 발생하여 애플리케이션이 멈출 수 있음 → 실행 빈도를 최소화해야 함!
    • Old GC 과정
    1. Old Gen이 가득 차면 Full GC 발생
    2. Mark 단계: 살아 있는 객체 마킹
    3. Sweep 단계: 사용되지 않는 객체 제거
    4. Compact 단계: 메모리 단편화 해결 (객체를 연속적으로 정렬)

   → Old GC는 실행 속도가 느리므로 Full GC 발생을 최소화하는 것이 중요!

4. Young Generation과 Old Generation으로 나눴을 때의 장점


GC의 실행 방식(아는 만큼 설명)

GC는 세대별 전략을 활용하여 최적화된 방식으로 실행됨!

  • Young GC는 Copying GC 방식으로 빠르게 실행됨!
  • Old GC는 Mark-Sweep-Compact 방식으로 실행되며, Full GC는 성능 저하 원인이 될 수 있음!
  • Java 8에서는 기본적으로 Parallel GC를 사용하지만, G1 GC를 선택하면 STW 시간을 줄일 수 있음!
  • GC 튜닝을 통해 Heap 크기와 GC 실행 방식을 최적화 가능!

Java 8과 Java 11의 디폴트 GC 실행 방식은 어떤 것일까?

버전기본 GC 알고리즘특징
Java 8Parallel GCThroughput(처리량) 최적화, 멀티 스레드 GC
Java 11G1 GCSTW(Stop-the-World) 최소화, 대용량 Heap 최적화

→ Java 8은 성능(처리량)에 초점, Java 11은 STW 시간 최소화에 초점!

G1 GC

G1 GC의 Heap 구조

┌──────────────────────────────────────────────┐
│              Heap Memory                 │
├───────────┬──────────┬────────────┬──────────┤
│  Region  │  Region │  Region   │  Region │
│  (Eden)  │  (Old)  │ (Survivor)│ (Old)   │
├───────────┴──────────┴────────────┴──────────┤
│  Young Gen   |   Old Gen   |   Humongous │
└──────────────────────────────────────────────┘

왜 Java 11은 디폴트 GC를 G1 GC로 변경했을까?

Java 8에서는 기본적으로 Parallel GC가 사용됐지만
Java 9부터 기본 GC가 G1 GC(Garbage First GC)로 변경됐고 Java 11에서도 유지됐음

그 이유는 STW(Stop-the-World) 시간을 줄이고 대규모 Heap을 효과적으로 관리하기 위해서 !

0개의 댓글