본문 바로가기

Language/Java

[GC] 가비지 컬렉터의 개념, 원리, 알고리즘

가비지 컬렉터(Garbage Collector)와 가비지 컬렉션(Garbage Colletion)
가비지 컬렉터 (Garbage Collector)
  • 정의 : 메모리 관리를 담당하는 시스템의 구성 요소
  • 역할 : 메모리에서 더 이상 사용되지 않는 객체(즉, 가비지)를 찾아 제거하여 메모리를 회수
  • 실행 방식 : 이 작업은 자동으로 수행되며, 프로그래머가 직접 객체의 메모리를 해제할 필요 없이 가비지 컬렉터 주기적으로 메모리에서 사용되지 않는 객체를 정리
가비지 컬렉션 (Garbage Collection)
  • 정의 : 메모리 관리 기술 중 하나로, 가비지 컬렉터에 의해 수행되는전체적인 프로세스
  • 역할 : 사용되지 않는 객체를 식별하고, 이를 메모리에서 제거하는 일련의 과정
    → 이 과정에는 객체 추적, 참조 카운트 검사, 메모리 회수 등이 포함
  • 즉, 가비지 컬렉터 실제 메모리를 정리하는 프로그램 또는 시스템이며, 가비지 컬렉션 정리 작업이 어떻게 이루어지는 지에 대한 전체 과정을 의미

사용하지 않는 객체를 정리해야하는 이유?
  • C와 C++에서 malloc이나 new로 할당한 메모리를 사용하지 않는 경우 free나 delete를 사용하여 명시적으로 메모리 해제가 필요
  • 만약 해제를 하지 않았을 경우 해당 객체가 메모리에 계속하여 남아있어 메모리 누수로 이어지고 시스템의 성능을 저하시키는 요인이 됨
  • Java에서는 이러한 메모리 누수를 방지하고 효율적인 메모리 관리를 수행하기 위해 JVM의 Garbage Collector가 자동으로 사용하지 않는 객체를 제거하고 메모리를 회수함으로써 개발자가 신경써야하는 부분이 줄어들게 됨

JVM Heap 메모리의 영역
  • JVM의 한 종류인 Hotspot JVM의 Heap 영역은 아래의 그림과 같음

 

Young Generation (Young 영역)
  • 역할
    • 새롭게 생성된 객체들이 할당되는 영역
    • 이 영역은 단기적으로 살아남는 객체들이 대부분
  • 특징
    • 대부분의 객체들이 사용된 후 금방 Unreachable 상태가 되어 가비지 컬렉션의 대상이 되므로, 객체가 금방 사라짐
  • 가비지 컬렉션
    • Young 영역에 대한 가비지 컬렉션Minor GC라고 함
    • Minor GC는 Young 영역에서만 수행되며, 빠르고 빈번하게 일어남
    • 객체의 생성과 사망 주기가 짧기 때문 빠르게 메모리 회수 및 정리가 필요
  • Young 영역의 구조
    • 보통 Young Generation은 Eden 영역Survivor 영역으로 나뉘게 됨
    • 새로 생성된 객체Eden 영역에 할당되고, 일정 조건을 만족하면 Survivor 영역으로 복사
Old Generation (Old 영역)
  • 역할
    • Young 영역에서 Reachable 상태를 유지한 객체들이 승격되어 복사되는 영역
    • 이 객체들은 더 이상 Young 영역에서 가비지 컬렉션을 받지 않으며, Old Generation에서 관리
  • 특징
    • Old 영역은 Young 영역보다 더 큰 메모리 공간을 할당받으며, 이 영역의 객체들은 오래 살아남은 객체들
    • 이 때문에 이 영역에서는 가비지 컬렉션이 자주 발생하지 않음
  • 가비지 컬렉션
    • Old 영역에 대한 가비지 컬렉션Major GC 또는 Full GC라고 함
    • Major GCYoung 영역뿐만 아니라 Old 영역을 대상으로 하며, GC가 수행되는 시간이 길고 시스템에 부하를 줄 수 있음
Permanent Generation (Perm 영역)
  • 역할
    • JVM에서 클래스 메타데이터를 저장하는 영역
    • 이 영역은 클래스 및 메서드 정보, 정적 객체 등 JVM에서 필요한 중요한 정보를 저장
  • 특징
    • 이 정보들은 JVM 실행 도중 변경되지 않으며, JVM 종료 시까지 계속 유지
  • 변경
    • Java 8 이후 Metaspace로 대체
    • Metaspace Perm 영역의 한계를 극복하고, 네이티브 메모리를 사용하여 더 많은 메타데이터를 저장할 수 있도록 개선 됨
  • Perm 영역에 저장되는 정보
    • 클래스의 메타 정보 : 클래스, 메서드, 필드 등의 정의
    • 정적 객체 : 클래스의 static 멤버 변수 및 메서드
    • JVM 내부 객체 : JVM의 최적화 컴파일러(JIT) 정보 등
Metaspace (Java 8 이후)
  • 변경 이유
    • Java 8부터 Perm 영역Metaspace로 대체
    • Metaspace힙 메모리 외의 네이티브 메모리를 사용하여 클래스 메타데이터를 저장하고, JVM의 메타데이터 공간을 더욱 효율적으로 관리
  • 특징
    • 동적으로 크기가 조정되며, 필요에 따라 메모리 공간이 확장
    • 이로 인해 Perm 영역에서 발생했던 메모리 부족 문제를 해결

Reachable vs Unreachable

오라클 HotSpot VM 기준의 런타임 데이터 영역

  • 자바 GC는 객체가 가비지인지 판별하기 위해서 Reachability라는 개념을 사용
  • Reachable (도달 가능한 객체)
    • 객체가 어떤 유효한 참조에 의해 접근할 수 있을 때 해당 객체는 "reachable" 상태로 간주
    • 즉, 현재 프로그램의 실행 흐름에서 해당 객체에 접근 가능을 의미
    • 예를 들어, 스택 프레임의 지역 변수다른 객체에서 참조된 객체reachable 객체
  • Unreachable (도달 불가능한 객체)
    • 객체가 어떤 유효한 참조로부터도 참조되지 않아 접근할 수 없을 때 해당 객체는 "unreachable" 상태로 간주
    • 이 객체는 더 이상 사용되지 않기 때문가비지 컬렉션(GC)의 대상
    • 즉, 프로그램에서 해당 객체에 접근할 방법이 없으므로 메모리에서 회수할 수 있습니다.
  • 객체 참조의 Root Set (루트 집합)
    • 자바에서는 root set을 기준으로 객체의 Reachability를 판별
    • 가비지 컬렉터가 객체의 참조 여부를 추적할 때 시작점이 되는 참조들을 의미

Root Set의 종류
  • Java 스택(스레드 스택)
    • 각 스레드는 독립적인 스택 메모리를 가지고 있으며, 여기서 지역 변수와 메서드 파라미터들이 힙 객체를 참조 가능
    • 이 스택에서 직접 참조되는 객체들reachable 상태
    • Ex) 메서드 A에서 생성된 객체는 해당 메서드가 실행되는 동안 스택에 존재하며, 이 메서드가 참조하는 객체는 reachable로 간주
  • 네이티브 스택(Java Native Interface, JNI)
    • JNI를 통해 네이티브 코드에서 생성된 객체에 대한 참조가 있을 수 있음
    • 이 참조 역시 root set으로 간주되어 네이티브 코드가 참조하는 객체들은 reachable로 간주
  • 메서드 영역의 정적 변수
    • 클래스가 로딩될 때 메서드 영역(Method Area)에 저장되는 정적 변수들heap 객체에 대한 참조를 유지 가능
    • 이들 역시 root set으로 간주

  • root set으로부터 시작한 참조 사슬에 속한 객체들은 Reachable 객체
  • 참조 사슬과 무관한 객체들이 Unreachable 객체로 GC 대상

Stop the world란?
  • GC가 실행될 때 JVM 애플리케이션의 모든 스레드가 멈추고, GC 작업을 위해 JVM이 독점적으로 시스템 자원을 사용하게 되는 상태
  • 이 상태에서는 애플리케이션에서 실행 중인 다른 스레드들이 작업을 멈추고, GC 작업이 완료될 때까지 기다림
  • GC 튜닝은 이러한 Stop the world 시간을 줄이는 것

 

Stop the World가 발생하는 이유
  • GC는 객체 참조 관계를 추적하고, 사용되지 않는 객체를 찾아 메모리를 회수하는 작업을 수행
  • 이 과정에서 모든 객체의 참조 관계를 정확히 파악해야 하기 때문에, 애플리케이션의 실행을 잠시 멈추고 참조 그래프를 안전하게 추적할 수 있어야 함
  • 객체 참조 추적
    • GC는 객체들이 서로 어떻게 참조되고 있는지 파악해야 함
    • 애플리케이션이 실행되는 동안 객체의 상태가 변할 수 있기 때문에 참조 관계를 추적하려면 애플리케이션이 일시적으로 멈추어야 함
  • 안전한 GC 실행
    • 애플리케이션이 실행되면서 객체의 참조 관계가 변경될 수 있음
    • Ex) 객체가 다른 객체를 참조하거나, 참조가 해제될 수 있음
      변경을 추적하면서 동시에 GC가 실행되면 잘못된 메모리 해제가 발생할 수 있으므로, 일시적으로 모든 작업을 멈추고 GC를 수행
  • 멀티스레드 안전성
    • JVM은 여러 스레드에서 동시 작업을 실행하므로 GC가 실행되는 동안 다른 스레드가 메모리를 변경하거나 참조를 수정하는 것을 방지해야 함
    • 따라서 모든 스레드를 일시 정지시켜서 GC가 안전하게 수행되도록 함

가비지 컬렉션 과정



Minor GC
  • Young Generation(주로 Eden 영역과 두 개의 Survivor 영역)을 대상으로 하는 가비지 컬렉션
  • 객체가 Young 영역에 할당된 후 일정 조건에 따라 Minor GC가 실행되고 그 과정을 반복하면서 객체가 Old 영역으로 이동
  • Young 영역의 동작 순서
    1. 새로운 객체 할당 (Eden 영역)
      • 객체가 처음 생성되면 Eden 영역에 할당
      • Eden 영역은 Young Generation에서 가장 빠르게 가득 차는 부분
    2. Eden 영역이 꽉 차면 Minor GC 발생
      • Eden 영역이 꽉 차게 되면 Minor GC 발생
      • 이 때 사용되지 않는 객체는 메모리에서 해제되고 Eden 영역에서 살아남은 객체는 한 개의 Survivor 영역으로 이동
    3. Survivor 영역
      • Young 영역에는 두 개의 Survivor 영역(S0, S1)이 존재
      • Minor GC가 실행될 때 Eden 영역에서 살아남은 객체한 개의 Survivor 영역으로 옮겨짐
      • Survivor 영역은 두 개가 있으며, 하나의 영역만 객체를 저장하고 다른 영역은 비어 있어야 함
      • 살아남은 객체는 Survivor 영역에서 계속 생존 가능
    4. Survivor 영역이 가득 차면
      • 살아남은 객체는 다른 Survivor 영역으로 옮겨짐
      • 이 때 한 개의 Survivor 영역은 반드시 비게 됨
      • 이 과정이 반복
    5. Old 영역으로의 Promotion
      • Survivor 영역에서 살아남은 객체일정 횟수 이상 살아남으면 Old 영역으로 Promotion
      • 이 객체들은 더 이상 Young Generation에서 가비지 컬렉션의 대상이 되지 않으며, Old Generation으로 이동해 더 긴 생애를 가질 가능성이 큼
  • 객체의 생존 횟수(Age)
    • Minor GC가 발생할 때마다 객체는 살아남은 횟수를 카운트
      → 이 정보를 객체의 헤더(Object Header)에 기록하는데, 이를 Age
    • 객체가 Survivor 영역으로 이동할 때마다 Age가 증가
      → Age는 객체가 얼마나 살아남았는지를 추적하는 데 사용
    • Age가 일정 값을 넘으면 객체는 Old Generation으로 Promotion
      → 이 값은 promotion threshold라고 하며, JVM의 설정에 따라 다를 수 있음
Major GC
  • Old Generation을 대상으로 하는 가비지 컬렉션을 의미
  • Old 영역이 가득 차게 되면 Major GC가 발생
  • 이 과정은 Generational GC의 일환으로 Old 영역에서 사용되지 않는 객체를 제거하고, 메모리 회수를 수행하는 중요한 역할
  • Old 영역에 대한 GC 방식들
    • Serial GC
    • Parallel GC
    • Parallel Old GC (Parallel Compacting GC)
    • Concurrent Mark & Sweep GC (CMS)
    • G1 GC (Garbage First GC)

마크 앤 스윕 (Mark and Sweep)
  • 가장 기본적인 가비지 컬렉션 알고리즘
  • 주로 Young 영역에서 사용되거나 전체 힙 영역에서 사용
  • Young 영역에서는 주로 객체가 금방 사라지는 특징이 있기 때문에 GC가 자주 발생하고 주로 Mark and Sweep을 이용하여 가비지를 처리
  • 객체를 지우는 것에 집중하지만 메모리 단편화(메모리가 조각나서 사용 불가능한 상태)가 발생할 수 있다는 단점 존재
    → 즉, 가비지가 삭제된 후에도 빈 공간이 남게 되어 힙 메모리 비효율적으로 사용될 수 있음

 

마크앤 스윕 과정
  1. Mark Phase (마크 단계)
    • Root Set으로부터 시작하여 모든 객체들을 순차적으로 탐색
    • Reachable 객체마킹, Unreachable 객체마킹 X
      Reachable 객체는 애플리케이션에서 여전히 사용되고 있는 객체이기 때문에 삭제하지 않으며,
      Unreachable 객체는 가비지로 간주하여 삭제할 준비
  2. Sweep Phase (스윕 단계)
    • Mark Phase에서 마킹되지 않은 객체들, 즉 Unreachable 객체들을 메모리에서 제거
    • 이를 통해 가비지가 메모리에서 회수됩니다.

마크-스윕-컴팩트 (Mark-Sweep-Compact)
  • Mark and Sweep을 확장한 알고리즘으로 스윕 이후 컴팩팅(compacting) 단계 추가
  • 이 단계는 메모리 단편화를 해결하기 위한 방법
  • Old 영역에서 자주 사용되는 알고리즘
    → Old 영역객체가 오래 살아남은 영역으로, 객체가 오래되면 가비지 컬렉션이 더 적게 발생
  • Old 영역에서는 메모리 단편화가 문제가 될 수 있기 때문에, 컴팩팅 단계가 포함된 Mark-Sweep-Compact 알고리즘이 더 효과적

 

마크-스윕-컴팩트 과정
  1. Mark Phase (마크 단계)
    • Mark and Sweep과 동일하게, Root Set으로부터 시작하여 Reachable 객체 마킹
  2. Sweep Phase (스윕 단계)
    • Mark Phase에서 마킹되지 않은 Unreachable 객체들 메모리에서 제거
  3. Compact Phase (컴팩트 단계)
    • Sweep이 끝난 후 남은 객체들을 한 곳으로 모음
    • 이 단계에서 사용되지 않는 메모리 공간 연속적인 블록으로 모아서 단편화 문제 해결
    • 이렇게 객체들이 모여서 연속된 메모리 블록을 만들게 되어, 메모리 단편화를 방지하고 힙 메모리의 효율성을 높이는 효과를 얻을 수 있음

 

참고 자료

https://velog.io/@yarogono/Java%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0Garbage-Collector%EB%9E%80

 

[Java]가비지 컬렉터(Garbage Collector)란?

JVM GC가 Heap에서 사용하지 않는 객체를 메모리를 해제해준다. 하지만 어떻게 동작하고 어떤 알고리즘을 사용 하는지 알아보지 못해서 글로 작성해봤다. 단순히 암기가 아닌 시각적인 사진들을

velog.io