시작
JVM에서 메모리 관리는 가비지 컬렉션을 통해 이루어집니다. 이 포스트에서는 가비지 컬렉션에 대해 알아보겠습니다.
본문
스택과 힙
JVM에서 메모리는 스택(Stack)과 힙(Heap)을 포함합니다.
- 스택은 로컬 변수, 메서드 호출, 리턴 값 등을 저장하는 공간입니다.
- 힙은 객체를 저장하는 공간입니다.
힙에 저장된 객체는 더 이상 사용되지 않을 때 메모리를 잘 관리하기 위해 가비지 컬렉션을 사용하여 메모리를 해제합니다.
마크 앤 스위프(Mark and Sweep)
마크 앤 스위프는 가비지 컬렉션의 가장 기본적인 알고리즘입니다.
그중 가장 간단한 형태는 다음과 같습니다.
- 모든 실행 중인 프로그램 스레드를 일시 정지합니다.
live
객체를 찾기 위해 힙을 순회하며live
객체를Mark
합니다.live
객체는 참조되고 있는 객체입니다.
- 남은 모든 것은 가비지로 간주하고 Sweep 단계를 수행합니다.
Sweep
단계는 마킹되지 않은 객체를 메모리에서 해제합니다.
Stop the World
마크 앤 스위프 형태의 가비지 컬렉션은 불가피하게 모든 스레드를 일시 정지시켜야 합니다. 이를 Stop the World
라고 합니다. 이로 인해 프로그램이 일시 정지되는 시간이 발생하게 됩니다.
세대별 가비지 컬렉션
Stop the World
문제를 해결하기 위해 세대별 가비지 컬렉션이 등장했습니다. 세대별 가비지 컬렉션은 객체의 수명을 세대로 나누어 관리합니다.
- Eden: 새로 생성된 객체가 저장되는 공간
- Survivor: 가비지 컬렉션 주기에서 생존한 객체가 저장되는 공간
- Tenured(Old): Survivor 영역에서 일정 횟수 이상 살아남은 객체가 저장되는 공간
세대별 가비지 컬렉션은 Minor GC
와 Major GC
로 나뉩니다.
Minor GC
(Young GC): Eden 및 Survivor 영역에서 일어나는 가비지 컬렉션- 마킹 단계 동안
live
객체를 찾아Survivor
영역으로 이동시킵니다. live
객체가Tenured
영역으로 이동될 때까지 반복합니다.- 다른
young live
객체는 빈Survivor
영역으로 이동합니다. - Eden과 지워진
Survivor
영역은 메모리에서 해제됩니다.
- 마킹 단계 동안
Major GC
(Full GC): Tenured 영역에서 일어나는 가비지 컬렉션compacting
: 오래된 객체를 메모리를 정리하는 과정
애플리케이션 코드가 힙의 내용을 수정할 수 있기 때문에 스레드를 임의의 시간에 컬렉션을 위해 중지시킬 수는 없습니다. 이를 위해 각 스레드마다 세이프포인트(safepoint
)를 설정합니다. 세이프
포인트는 스레드가 안전하게 중지될 수 있는 지점을 의미합니다. 모든 스레드가 세이프포인트에 도달하면 가비지 컬렉션이 시작됩니다.
G1 가비지 컬렉션
G1(Garbage First) 가비지 컬렉션은 세대별 가비지 컬렉션의 단점을 보완하기 위해 등장했습니다. G1은 힙을 동일한 크기의 리전(Region)으로 나누어 관리합니다. 각 리전은 Eden, Survivor,
Old 영역을 포함합니다.
G1은 일시 중단 목표(pause time goal
)를 설정하여 가비지 컬렉션을 수행합니다. 이를 통해 Stop the World
시간을 줄일 수 있습니다.
G1은 오래된 세대로 이동한 객체를 추적하고 오래된 세대의 공간이 가득 차면 오래된 컬렉션을 수행합니다. 이는 애플리케이션 스레드와 가능한 한 동시에 가비지 컬렉션을 수행되므로 동시성 컬렉션이라고 할 수 있다.
- 이 오래된 컬렉션의 첫 번째 단계는 동시 마킹 단계입니다. 이 단계에서는
live
객체를 찾아 마킹합니다. - 첫 번째 단계가 끝나면 즉시
young GC
를 수행합니다. - 그다음은 혼합 컬렉션
mixed collection
이 수행됩니다. 이 단계에서는live
객체를 찾아 마킹하고live
객체를 새로운 리전으로 이동시킵니다. - 마지막 단계는
compacting
단계입니다. 이 단계에서는live
객체를 이동시키고 빈 리전을 해제합니다.
단일 리전보다 큰 크기의 객체를 할당할 수도 있는데 이러한 객체는 특수한 유형의 리전인 거대한 리전(Humongous Region)에 할당됩니다.
배열을 할당할 수 있는 메모리 공간이 없는 경우에는 메모리가 조각화됐다(fragmented)고 합니다.
parallel GC는 멀티코어 프로세서에서 가비지 컬렉션을 병렬로 수행하는 방법입니다. 하지만 Parallel GC가 매우 효율적인 컬렉터지만 Stop the World를 해결하는 것은 아닙니다.
가비지 컬렉션 구성 매개변수
가비지 컬렉션은 다양한 구성 매개변수를 가지고 있습니다. 이러한 매개변수는 가비지 컬렉션의 동작을 제어하는 데 사용됩니다.
-Xms<heap size>m
: 초기 힙 크기입니다. 최근에는Xmx
를 사용하여 초기 힙 크기를 설정하지 않습니다.-Xmx<heap size>m
: 최대 힙 크기입니다.-Xmn<young size>m
: Young 영역 크기입니다.-XX:-DisableExplicitGC
:System.gc()
를 사용하지 못하게 합니다. 이는 가비지 컬렉션을 수동으로 실행할 수 없게 합니다.-XX:MaxGCPauseMillis=<pause time>
: G1 가비지 컬렉션의 일시 중단 목표 시간입니다.-XX:GCPauseIntervalMillis=<pause interval>
: G1 가비지 컬렉션의 일시 중단 간격 시간입니다.
마무리
이 포스트에서는 가비지 컬렉션에 대해 알아보았습니다. 가비지 컬렉션은 메모리 관리를 위해 중요한 요소이며 성능 튜닝에 있어서 중요한 요소 중 하나입니다. JAVA에서 가비지 컬렉션에 대한 연구가 계속되고 있으며
앞으로 더 많은 발전이 있을 것이라고 알 수 있습니다.
댓글