
위의 그림은 JVM(Java Virtual Machine)의 구성 요소이다.
new 명령어를 통해 생성된 새로운 인스턴스는 heap 메모리 영역에 저장된다.
이후 JVM은 GC를 통해, 더이상 참조하지 않는 인스턴스를 찾아 heap 메모리 영역에서 해제시킨다.
따라서 개발자가 메모리를 명시적으로 관리할 필요가 없어 편리하다.

heap 영역은 위와 같이 크게 세 영역(Young Generation, Old Generation, Permanent Generation)으로 나뉘어진다.
결론적으로는 더욱 효율적인 메모리 관리를 위해서다.
대부분의 객체는 금방 unrechable(참조가 끊긴 상태)된다.
즉, 일부 객체만이 오랫동안 참조 상태를 유지한다는 것이며, 해당 객체들을 Old Generation 영역에 따로 보관하여 관리하는 것이 더 효율적이다.
Young Generation 영역에서의 GC(Minor GC)는 빈번하게 수행되고 적은 메모리 공간을 차지하기 때문에, 속도가 핵심이다.
Old Generation 영역에서의 GC(Major GC)는 많은 메모리 공간을 차지하기 때문에 공간 효율성이 핵심이다.
인스턴스가 새로 생성되면, 가장 먼저 eden 영역에 할당된다.
이후 계속된 인스턴스 생성으로 인해 eden 영역이 가득차면 Minor GC가 발생하는데, 참조중인 인스턴스는 survivor 영역 중 하나로만 이동하고(Survivor 영역은 1영역과 2영역으로 나누어지는데, 두 영역 중 하나는 비어있어야한다.) 참조가 없는 인스턴스는 eden 영역에서 제거된다.
만약, 인스턴스가 이동하려는 survivor 영역의 남은 영역보다 크기가 크면 old 영역으로 이동한다.
한 survivor 영역이 가득차면 또다시 Minor GC가 발생하고, 참조중인 인스턴스는 다른 survivor 영역으로 이동하고 참조가 없는 인스턴스는 제거된다.
만약, 다른 survivor 영역으로 이동하는 인스턴스가 남아있는 survivor 영역보다 크기가 크면 old 영역으로 이동한다.
그리고 한 survivor 영역에서 다른 survivor 영역으로 이동할 때마다 age 값이 증가되고 이를 각 객체의 Header에 저장한다.
Young Generation 영역에 공간이 없다면, survivor 영역에서 age가 높은 인스턴스가 old 영역으로 이동한다. (=promotion)
old 영역에 존재하는 모든 인스턴스를 스캔 후, 더이상 참조되지 않는 인스턴스를 제거한다.
old 영역은 많은 메모리를 차지하기 때문에, Major GC는 속도가 매우 느리다.
또한, Major GC때마다 Compaction 작업이 수행되어 공간 효율성을 극대화한다.
(Compaction 작업은 뒤에서 알아보자.)
OutOfMemoryError가 발생할 수 있다.GC를 수행하는 동안에는 GC 스레드를 제외한 모든 스레드들은 일시정지되는데, 이를 stop the world라 부른다.
Major GC의 경우 Minor GC보다 시간이 많이 소요되어, Major GC의 빈도수와 시간은 애플리케이션의 성능과 안정성에 아주 큰 영향을 미친다. GC 성능 개선을 위해 튜닝을 하면, Major GC의 시간을 줄이는 것이 핵심이다.
또한, JVM에서도 이러한 문제를 해결하기 위해 다양한 GC 알고리즘 옵션을 제공하고 있다.

위치가 가까운 객체라고 해서 같은 시점에 메모리에서 해제되지는 않는다.
즉, GC를 거듭하면서 메모리 파편화(메모리 내에 비어있는 공간이 서로 떨어져있어 큰 객체를 할당하기 어려운 상태)가 발생하여 메모리 공간 효율성이 떨어지게된다.
따라서 이때 수행되는 compact 작업은 Major GC가 진행되고 살아남은 참조중인 인스턴스들을 heap 메모리 영역의 아래쪽으로 모으는 작업이다.
해당 작업으로 인해 크기가 큰 객체를 메모리에 할당하기 쉬워진다.
GC 성능 문제 해결을 위해, JVM에서는 다양한 GC 알고리즘을 옵션으로 제공하고 있다.
(JVM 버전별로 지원하는 알고리즘이 다를 수 있다.)
heap 메모리 영역을 바둑판처럼 나누어 관리한다.
Reference