메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능이다. C, C++ 등의 프로그래밍 언어에서는 수동으로 메모리를 관리해야 하지만 Java나 Kotlin을 이용해 개발을 하면 JVM의 가비지 컬렉터(GC)가 불필요한 메모리를 알아서 정리 해주기 때문에 Java 프로세스가 한정된 메모리를 효율적으로 사용할 수 있게 해주고, 개발자 입장에서 메모리 관리, 메모리 누수 문제를 관리하지 않아도 돼서 개발에만 집중할 수 있는 장점이 있다.
단점으로는 개발자가 메모리가 언제 해제되는지 정확하게 알 수 없어서 제어하기 힘들고, 가비지 컬렉션(GC)이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생한다. 오버헤드가 자주 발생하게 되면 성능 하락의 문제가 되기도 한다.
Java에서도 System.gc()를 이용해 호출할 수 있지만,
오버헤드가 굉장히 크므로 가비지 컬렉션(GC)에게 메모리 해제를 맡기는 것이 좋다.
메모리를 수동으로 관리하던 것에서 비롯된 에러를 예방할 수 있다.
가비지 컬렉션(GC)는 객체에 레퍼런스가 있다면 Reachable로 구분하고, 객체에 유효한 레퍼런스가 없다면 Unreachable로 구분 후 수거한다.
객체들은 실질적으로 Heap 영역에서 생성되고 Method Area이나 Stack Area에서는 Heap Area에 생성된 객체의 주소만 참조하는 형식으로 구성된다. 하지만 이렇게 생성된 Heap Area의 객체들이 메서드가 끝나는 등의 특정 이벤트들로 인하여 Heap Area 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되는 현상이 발생하면, 위의 그림에서 빨간색 객체(Unreachable)와 같이 Heap 영역에서 어디든 참조하고 있지 않은 객체들이 발생한다. 이러한 객체들을 주기적으로 가비지 컬렉터가 제거해준다.
Garbage Detection에 초첨이 맞추어진 초기 알고리즘이다. 각 객체마다 Reference Count를 관리하여 Reference Count가 0이 되면 가비지 컬렉션(GC)을 수행한다.
이 방식은 각 객체마다 Reference Count를 변경해 주어야 하기 때문에 그에 대한 관리 비용이 크고, 참조를 많이 하고 있는 객체의 Reference Count가 0이 될 경우 연쇄적으로 가비지 컬렉션(GC)가 발생할 수 있는 문제가 있다. 또한 메모리 누수가 발생할 가능성도 크다.
Reference Counting 알고리즘의 단점을 극복하기 위해 나온 알고리즘이다. 가비지 컬렉션(GC)는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서 각각 어떤 객체를 참고하는지 탐색한다. 그리고 사용되고 있는 메모리를 식별(Mark)하고 식별되지 않은 객체들을 메모리에서 제거(Sweep)한다.
별도의 오버헤드가 없어서 성능이 비교적 좋지만, 가비지 컬렉션(GC)가 수행되는 도중 Mark 작업의 정확성과 Memory Corruption을 방지하기 위해 Heap의 사용이 제한되기 때문에 지연(Suspend) 현상이 발생한다. 그리고 객체들이 지워진 공간이 분열된(fragmentation) 것으로 남게 되어 메모리 할당이 불가능한 상태가 된다.
Mark and Sweep 알고리즘의 fragmentation 약점을 극복하기 위해 나온 알고리즘이다. Sweep이 사라진 것이 아니고 Compact 안에 포함되어 있다. Mark and Compact 알고리즘은 기존의 Sweep 과정까지는 동일 하지만 Compact 과정을 더 거치게 된다.
Sweep 후에 분산된 객체들을 Heap 영역의 시작 주소로 모아 메모리가 할당된 부분과 fragmentation한 부분을 압축해서 메모리 공간의 효율을 높일 수 있다. 하지만 Compact 작업 이후 할당된 사용되는 메모리들의 Reference를 업데이트 하는 작업이 필요하기 때문에 부가적인 오버헤드가 발생한다.
![]()
(이미지 출처 : https://coding-factory.tistory.com/829)
JVM의 Heap 영역은 동적으로 레퍼런스 데이터가 저장되는 공간으로, 가비지 컬렉션(GC)에 대상이 되는 공간이다. Heap 영역은 처음 설계될 때 다음의 2가지를 전제(Weak Generational Hypothesis)로 설계 되었다.
즉, 객체는 대부분 일회성되며, 메모리에 오랫동안 남아있는 경우는 드물다. 그렇기 때문에 객체는 생존 기간에 따라 물리적인 Heap 영역을 나누게 되고 Young, Old 총 2가지 영역으로 설계되었다. 초기에는 Perm 영역도 존재했지만 Java 8부터 제거되었다.
Young 영역은 더욱 효율적인 GC를 위해 3가지 영역으로 나눈다.
- Eden
- new를 통해 사로 생성된 객체가 위치
- 정기적인 쓰레기 수집 후 살아남은 객체들은 Survivor 영역으로 보냄
- Survivor 0 / Survivor 1
- 최소 1번의 가비지 컬렉션(GC) 이상 살아남은 객체가 존재하는 영역
- Suvivor 영역에는 특별한 규칙이 있는데 Suvivor 0 또는 1 둘 중 하나는 꼭 비어 있어야 한다.
Old 영역이 Young 영역보다 크게 할당되는 이유는 Young 영역의 수명이 짧은 객체들은 큰 공간을 필요로 하지 않으며 큰 객체들은 Young 영역이 아니라 바로 Old 영역에 할당되기 때문이다.

(이미지 출처 : https://wooody92.github.io/java/GC-%EB%8F%99%EC%9E%91%EC%9B%90%EB%A6%AC/)
Young 영역은 일반적인 Old 영역보다 크기가 작기 때문에 가비지 컬렉션(GC)가 보통 0.5초에서 1초 사이에 끝난다. 그렇기 때문에 Minor GC는 애플리케이션에 크게 영향을 주지 않지만 Old 영역의 Major GC는 일반적으로 Minor GC 보다 시간이 오래 걸리며, 10배 이상의 시간을 사용한다.
| GC 종류 | Minor GC | Major GC |
|---|---|---|
| 대상 | Young Generation | Old Generation |
| 실행 시점 | Eden 영역이 꽉 찬 경우 | Old 영역이 꽉 찬 경우 |
| 실행 속도 | 빠르다 | 느리다 |
JVM에서는 애플리케이션과 가비지 컬렉션(GC)을 병행하여 실행할 수 있는 여러 옵션들을 제공한다. 다양한 가비지 컬렉션(GC)가 어떠 방식으로 애플리케이션 실행과 병행되는지 살펴 보기 전에 Stop The World(STW) 개념을 알아야 한다. Stop The World란 가비지 컬렉션(GC)을 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것을 말한다.

-XX:+UseSerialGCGC 옵션을 지정하여 해당 가비지 컬렉션 알고리즘으로 힙 메모리를 관리하도록 실행할 수 있다.java -XX:+UseSerialGC -jar Application.java

java -XX:+UseParallelGC -java Application.java
# -XX:ParallelGCThreads=N -> 사용할 스레드 갯수
java -XX:+UseParallelOldGC -jar Application.java

# Java 9 버전부터 Deprecated 되었고 Java 14 에서는 사용이 중지
java -XX:+UseConcMarkSweepGC -jar Application.java

java -XX:+UseG1GC -jar Application.java
위키백과 가비지 컬렉션
https://mangkyu.tistory.com/118
https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC
https://coding-factory.tistory.com/829
https://steady-coding.tistory.com/584
https://yoon1fe.tistory.com/152