
Oracle의 공식문서 HotSpot Virtual Machine Garbage Collection Tuning Guide을 참고했습니다.
📍 가비지 컬렉션(Garbage Collection)이란?
📍 가비지 컬렉터(Garbage Collector)가 필요한 이유?
📍 가비지 컬렉터(Garbage Collector) 선택 기준
Java 애플리케이션은 다양한 환경에서 실행될 수 있다. 작은 데스크톱 애플리케이션부터 대형 서버에서 운영되는 웹 서비스까지 폭넓게 활용되기 때문에, Java HotSpot 가상 머신(HotSpot VM)은 여러 가지 가비지 컬렉터(GC)를 제공하여 서로 다른 요구 사항을 충족할 수 있도록 한다.
일반적으로 Java SE는 실행 환경에 따라 가장 적절한 가비지 컬렉터를 자동으로 선택하지만, 특정 애플리케이션의 성능 목표에 맞춰 최적화하려면 직접 GC를 선택하고 튜닝해야 한다.
이 문서에서는 GC의 기본 개념과 다양한 GC 알고리즘의 특성을 설명하고, 각 GC의 선택 기준과 튜닝 방법을 다룬다.
JVM은 Java 프로그램을 실행하기 위한 가상 머신이다.
자바 코드(바이트 코드)를 운영체제와 하드웨어에 독립적으로 실행할 수 있도록 해준다.
JVM의 주요 역할
HotSpot VM은 JVM의 구현체 중 하나이다.
즉, HotSpot VM은 하나의 JVM으로 Oracle과 OpenJDK에서 기본적으로 제공하는 JVM이다.
HotSpot VM의 특징
다른 JVM 구현체로는 OpenJ9, GraalVM, Zulu, Corretto, Liberica 등이 있다.
JVM은 인터페이스라기보다는 명세(Specification) + 구현(Implementation)을 포함하는 개념이다.
명세 자체는 구현이 아니라 설계도이다.
JVM이 “자동차”라면, HotSpot VM은 “특정 브랜드(예: BMW) 자동차”라고 볼 수 있다.
가비지 컬렉터란 애플리케이션의 동적 메모리 할당을 자동으로 관리하는 시스템이다.
GC는 다음과 같은 작업을 수행한다.
프로그램이 실행되면 운영체제(OS)는 Java 가상 머신(JVM)에 일정량의 메모리를 할당한다.
JVM은 이 메모리를 힙(heap) 메모리로 사용하며, 애플리케이션이 필요할 때마다 객체를 생성하여 여기에 저장한다.
GC는 이 힙 영역에서 사용되지 않는 객체를 찾아 제거하고, 필요할 경우 운영체제에 메모리를 반환하기도 한다.
운영체제는 프로세스 단위로 메모리를 관리하지만, JVM은 애플리케이션이 생성하는 개별 객체 단위로 관리해야 하기 때문이다.
Java 애플리케이션이 new 키워드를 사용하여 객체를 생성하면, JVM은 힙 영역에서 메모리를 찾아 해당 객체에 할당한다.
만약 메모리가 부족하다면 GC가 동작하여 불필요한 객체를 제거하고, 새로운 객체를 위한 공간을 확보한다.
애플리케이션이 계속해서 객체를 생성하면 결국 메모리가 가득 차고, 더 이상 객체를 생성할 수 없는 상황이 발생하기 때문이다.
GC는 객체가 여전히 사용 중인지 아닌지를 판단해야 한다.
이를 위해 Java는 참조(reference) 개념을 사용한다.
어떤 객체에 대한 참조가 남아 있다면 해당 객체는 아직 필요하다고 간주되며, 참조가 하나도 남아 있지 않다면 GC가 해당 객체를 제거할 수 있다.
단순히 객체가 오래되었다고 삭제하면, 여전히 필요한 객체까지 삭제할 위험이 있기 때문이다.
GC는 사용되지 않는 객체를 삭제하고, 해제된 메모리를 새로운 객체 생성을 위해 재사용할 수 있도록 한다.
이 과정이 자동으로 이루어지기 때문에 개발자는 메모리 관리를 직접 신경 쓸 필요가 없다.
개발자가 수동으로 메모리를 관리하면 실수가 발생할 가능성이 크기 때문이다.
또한, GC가 자동으로 수행하면 코드가 간결해지고 유지보수가 쉬워진다.

→ 가비지 컬렉션은 힙 영역을 관리
GC는 애플리케이션이 실행되는 동안 동적으로 생성된 객체를 관리하고, 필요하지 않은 객체를 제거하는 역할을 한다.
GC가 없다면 개발자가 직접 메모리를 할당하고 해제해야 하는데, 이는 실수로 이어질 가능성이 크다.
예를 들어, C와 같은 언어에서는 사용한 메모리를 직접 해제해야 하며, 해제하지 않으면 메모리 누수(memory leak) 가 발생할 수 있다.
반대로, 잘못 해제하면 더블 프리(double free) 오류나 댕글링 포인터(dangling pointer) 오류가 발생할 수도 있다.
댕글링 포인터(dangling pointer) 오류: 해제된 메모리를 가리키고 있는 포인터
개발자가 직접 메모리를 관리하는 것은 어렵고 위험하기 때문이다.
메모리 누수와 같은 문제를 자동으로 해결하기 위해 GC가 등장했다.
HotSpot 가비지 컬렉터는 여러 가지 기술을 활용하여 GC 성능을 향상시킨다.
가비지 컬렉터를 사용하면 개발자가 직접 메모리를 관리하지 않아도 되므로, 메모리 누수(memory leak)나 잘못된 해제(double free)와 같은 문제를 방지할 수 있다.
그러나, GC가 실행되는 동안 애플리케이션의 실행이 일시적으로 멈출 수 있으며, 이로 인해 성능 저하가 발생할 수도 있다.
따라서, GC의 선택이 중요한 경우가 있다.
일반적인 애플리케이션은 적절한 GC를 자동으로 선택해도 문제없이 실행된다.
예를 들어, 가비지 컬렉션으로 인해 가끔 짧은 정지 시간이 발생하더라도 성능에 큰 영향을 주지 않는다면 기본 설정을 유지해도 된다.
다음과 같은 조건에서는 적절한 GC를 선택하고 튜닝하는 것이 성능에 큰 영향을 미칠 수 있다.
Amdahl의 법칙(Amdahl’s Law)에 따르면, 애플리케이션의 성능 향상은 병렬화할 수 없는 코드에 의해 제한된다.
즉, 병렬화가 가능한 부분이 많더라도, GC와 같은 순차적 작업이 병목이 되면 전체 성능이 저하될 수 있다.
아래 그래프는 GC가 전체 실행 시간의 1% 또는 10%를 차지할 때, CPU 코어 수가 증가함에 따라 성능이 얼마나 저하되는지를 보여준다.

이를 통해, 작은 시스템에서는 GC로 인한 문제를 쉽게 간과할 수 있지만, 대규모 시스템에서는 GC 성능이 중요한 요소가 된다는 것을 알 수 있다.
따라서, 시스템의 규모가 커질수록 적절한 GC 선택과 튜닝이 필요하다.
각 애플리케이션의 요구 사항에 맞춰 적절한 GC를 선택하고 튜닝하는 것이 중요하다.
참고문헌
🔗 Oracle 공식문서
🔗 JVM 메모리 구조
프로젝트 사후 관리 잘 부탁드립니다!