목표
Java의 가장 널리 알려진 장점은 "JVM을 통해 모든 OS에서 동작한다"는 것이지만 "Java는 OS로부터 할당받은 메모리를 자동으로 관리한다"는 매우 큰 장점 또한 존재한다. Java에서 메모리를 자동으로 관리하는 관리자는 누구이며 어떤 방식으로 관리하는 것인지 알아보자.
Java 메모리 관리자 GC(Garbage Collector)
GC(Garbage Collector)
Java GC는 JVM의 Execution Engine 내부에 존재하는데, OS로부터 할당 받은 메모리 영역(JVM Memory)을 자동으로 관리하는 기능이다. GC는 동적으로 할당된 메모리 영역(Heap)에서 더 이상 사용되지않는 객체를 식별하고 제거하여 메모리를 회수한다. 이를 통해 명시적으로 메모리 관리를 수행하지 않아도 되므로 Memory Leak이나 잘못된 메모리 사용으로 인한 문제를 줄일 수 있다.
GC는 Stack에서의 도달 가능성(Reachability)을 체크해 메모리를 회수한다. 오브젝트를 도달 가능한 객체(reachable object)와 도달 불가능한 객체(unreachable object)로 구분하는데 도달 가능한 객체는 Stack에서 최소한 하나의 참조로부터 접근 가능한 객체를 말하며 도달 불가능한 객체는 어떠한 참조도 없어 접근할 수 없는 객체를 말한다.
Java에서 일반적으로 사용하는 세가지 GC
1. Serial GC(직렬 GC)
Serial GC는 단일 스레드로 동작하는 가장 간단한 GC이다. 애플리케이션 실행을 일시 중지하고 메모리를 청소한다. 작은 Heap 크기에서는 성능이 좋을 수 있으나 GC 프로세스를 위해서는 모든 스레드를 중지시켜야하기 때문에 단일 스레드로 동작하는 Serial GC는 대규모 애플리케이션에서는 일시 중지 시간이 길어지는 단점이 있다. -XX:+UseSerialGC 옵션을 사용해 활성화 할 수 있다.
2. Parallel GC(병렬 GC)
Parallel GC는 Serial GC의 멀티스레드 버전이다. 여러 개의 스레드를 사용하여 GC를 병렬 처리하므로, Serial GC보다 빠른 수행 속도를 제공한다. Parallel GC 또한 Serial GC와 마찬가지로 애플리케이션 실행을 일시 중지하는 STW(Stop-the-World) 방식을 사용하므로 일시 중지 시간이 발생한다.
3. CMS(Concurrent Mark and Sweep) GC
CMS GC는 대규모 애플리케이션에서 사용되는 GC로, 일시 중지 시간을 최소화하기 위해 설계되었다. 하지만, JDK 14부터는 완전히 제거되었다고 한다.
이 외에도 G1(Garbage-First) GC와 ZGC(Shenandoah Garbage Collector) 등 특정 시나리오나 요구사항에 적합한 성능 및 특성을 가지고 있는 GC가 있으며 Java 버전과 GC 옵션을 조정하여 가장 적합한 GC를 선택해 사용한다고 한다. 위 GC 중 가장 기본적으로 사용되는 GC는 Parallel GC라고 한다. 코드와 Stack, Heap 영역의 간단한 그림을 보면서 GC의 기본적인 개념을 이해해보자.
public class Main {
public static void main(String[] args) {
String str = "thinking";
str += "potato";
}
}
str변수에 "thinking"을 저장하게 되었을때의 Stac, Heap 영역의 그림이다. str 변수가 "thinking"이라는 String 오브젝트를 참조하고 있다.
str += "potato"를 했을때의 결과이다. += 구분을 실행하게 되면 기존 Heap 영역의 데이터에 단순히 값을 추가하는 것이 아니라 Heap 영역에 새로운 String 오브젝트을 생성하게 되며 str변수는 새롭게 String 오브젝트를 참조한다.
처음 생성하였던 "thinking"이라는 문자열이 Heap영역에서 삭제되었음을 볼 수 있다. 이는 GC가 Garbage Collection을 발생시켜 Stack에서 도달할 수 없는 Heap 영역의 오브젝트를 메모리에서 제거한 것이다.
Garbage Collection(GC 프로세스) 동작방식
GC의 공통적인 동작 방식은 아래 2가지 단계를 따른다.
- STW(Stop-the-World)
- Mark and Sweep
STW(Stop-the-World)
STW는 GC 프로세스를 실행하기 위해 현재 실행중인 모든 스레드를 중단하는 작업이다. 모든 스레드를 중단하면 애플리케이션이 멈추기 때문에 성능에 영향을 준다. 보통 GC의 성능 개선을 위해 튜닝을 한다고 하면 보통 STW의 시간을 줄이는 작업을 하는 것이고 JVM에서도 이러한 문제를 해결하기 위해 다양한 실행 옵션을 제공하고 있다.
Mark and Sweep
GC 프로세스는 Mark and Sweep 이라고 부르기도 한다. Mark는 GC가 Stack의 모든 변수를 스캔하면서 각각의 변수가 Heap 영역의 어떤 오브젝트를 참조하고 있는지 찾아 marking 하는 과정이며 연결된 Stack, Heap 영역의 오브젝트 쌍이 marking 된다. marking을 진행하면서 앞서 설명한 stop the world가 발생한다. Sweep은 marking 되지 않은 오브젝트를을 Heap에서 제거하는 과정이다. Garbage Collector는 사용하지 않은 오브젝트를 모아서 지우는 것처럼 보이지만 사실상 사용하는 오브젝트를 마킹하고 나머지 사용하지 않는 오브젝트를 모두 지우는 방식으로 동작한다.
[ 참고자료 ]
https://yaboong.github.io/java/2018/06/09/java-garbage-collection/
https://ttl-blog.tistory.com/368