프로그램을 개발 하다 보면 유효하지 않은 메모리인 가바지(Garbage)가 발생하게 된다. C언어를 이용하면 free()라는 함수를 통해 직접 메모리를 해제해주어야 한다. 하지만 Java나 Kotlin을 이용해 개발을 하다 보면 개발자가 메모리를 직접 해제해주는 일이 없다. 그 이유는 JVM의 가비지 컬렉터가 불필요한 메모리를 알아서 정리해주기 때문이다. 대신 Java에서 명시적으로 불필요한 데이터를 표현하기 위해서 일반적으로 null을 선언해준다. 예를 들어 아래와 같은 코드가 있다고 가정하자.
Person person = new Person();
person.setName("Mang");
person = null;
// 가비지 발생
person = new Person();
person.setName("MangKyu");
기존의 Mang으로 생성된 person 객체는 더이상 참조를 하지 않고 사용이 되지 않아서 Garbage(가비지)가 되었다. Java나 Kotlin에서는 이러한 메모리 누수를 방지하기 위해 가비지 컬렉터(Garbage Collector, GC)가 주기적으로 검사하여 메모리를 청소해준다.
자바 GC는 객체가 가비지인지 판별하기 위해서 Reachability라는 개념을 사용한다.
어떤 객체에 유효한 참조가 있으면 ‘Reachable’로, 없으면 ‘Unreachable’로 구별한다.

위 사진은 오라클 HotSpot VM 기준에 런타임 데이터 영역이다.
(스레드가 차지하는 영역, 객체를 생성 및 보관하는 큰 힙, 클래스 정보가 차지하는 메서드 영역)
Object는 객체이고, 화살표는 참조를 나타낸다.
여기서 보면 알 수 있듯이 객체들의 참조는 사슬처럼 이루어져 있다.
이런 상황에서 유효한 참조 여부를 파악하려면 항상 최초의 참조가 있어야 하는데,
이를 객체 참조의 root set이라고 한다.
힙에 있는 객체들에 대한 참조는 다음 4가지 종류 중 하나이다.
이들 중 ‘힙 내의 다른 객체에 의한 참조’를 제외한 3 가지는 root set으로 reachability를 판가름하는 기준이 된다.

Young 영역과 Old 영역은 서로 다른 메모리 구조로 되어 있기 때문에, 세부적인 동작 방식은 다르다. 하지만 기본적으로 가비지 컬렉션이 실행된다고 하면 다음의 2가지 공통적인 단계를 따르게 된다.
Stop The World는 가비지 컬렉션을 실행하기 위해 JVM이 애플리케이션의 실행을 멈추는 작업이다. GC가 실행될 때는 GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되고, GC가 완료되면 작업이 재개된다. 당연히 모든 쓰레드들의 작업이 중단되면 애플리케이션이 멈추기 때문에, GC의 성능 개선을 위해 튜닝을 한다고 하면 보통 stop-the-world의 시간을 줄이는 작업을 하는 것이다. 또한 JVM에서도 이러한 문제를 해결하기 위해 다양한 실행 옵션을 제공하고 있다.
Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서 각각이 어떤 객체를 참고하고 있는지를 탐색하게 된다. 그리고 사용되고 있는 메모리를 식별하는데, 이러한 과정을 Mark라고 한다. 이후에 Mark가 되지 않은 객체들을 메모리에서 제거하는데, 이러한 과정을 Sweep라고 한다.

Young Generation 영역은 짧게 살아남는 메모리들이 존재하는 공간이다. 모든 객체는 처음에는 Young Generation에 생성되게 된다.
Young Generation의 공간은 Old Generation에 비해 상대적으로 작기 때문에 메모리 상의 객체를 찾아 제거하는데 적은 시간이 걸린다. (작은 공간에서 데이터를 찾으니까)
이 때문에 Young Generation 영역에서 발생되는 GC를 Minor GC라 불린다.
처음 생성된 객체는 Young Generation 영역의 일부인 Eden 영역에 위치


객체가 계속 생성되어 Eden 영역이 꽉차게 되고 Minor GC가 실행

Mark 동작을 통해 reachable 객체를 탐색

Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동

Eden 영역에서 사용되지 않는 객체(unreachable)의 메모리를 해제(sweep)

살아남은 모든 객체들은 age값이 1씩 증가

※ [ age 값이란? ]
Survivor 영역에서 객체가 살아남은 횟수를 의미하는 값이며, Object Header에 기록된다.
만일 age 값이 임계값에 다다르면 Promotion(Old 영역으로 이동) 여부를 결정한다.
JVM 중 가장 일반적인 HotSpot JVM의 경우 이 age의 기본 임계값은 31이다.
객체 헤더에 age를 기록하는 부분이 6 bit로 되어 있기 때문이다.
또한 Survivor 영역의 제한 조건으로 Survivor 영역 중 반드시 1개는 사용되어야 하고, 나머지는 비어 있어야 한다.
만약 두 Survivor 영역에 모두 데이터가 존재하거나, 모두 사용량이 0이라면 현재 시스템이 정상적인 상황이 아니라는 반증이 된다.
또다시 Eden 영역에 신규 객체들로 가득 차게 되면 다시한번 minor GC 발생하고 mark 한다



marking 한 객체들을 비어있는 Survivor 1으로 이동하고 unreachable한 객체는 메모리를 해제(sweep)


다시 살아남은 모든 객체들은 age가 1씩 증가

이러한 과정을 반복


Old Generation은 길게 살아남는 메모리들이 존재하는 공간이다.
Old Generation의 객체들은 거슬러 올라가면 처음에는 Young Generation에 의해 시작되었으나, GC 과정 중에 제거되지 않은 경우 age 임계값이 차게되어 이동된 녀석들이다.
그리고 Major GC는 객체들이 계속 Promotion되어 Old 영역의 메모리가 부족해지면 발생하게 된다.
※ Promotion: age 값이 임계값에 다다라서 Old 영역으로 이동 여부를 결정하는 것
객체의 age가 임계값(여기선 8로 설정)에 도달하게 되면

이 객체들은 Old Generation으로 이동된다. 이를 promotion이라 부른다.

위의 과정이 반복되어 Old Generation 영역의 공간(메모리)가 부족하게 되면 Major GC가 발생하게 된다.

다음 블로그에 정리된 내용을 참고하는게 좋을 것 같다.
https://vsfe.tistory.com/22
해당 블로그 내용을 요약하자면
결론은 변수의 값이 null로 설정되었다는 사실만으로 GC의 대상이 되는 것은 아니다. 객체에 대한 참조 여부가 GC 가능성을 결정한다.
Object obj = new Object();
Object anotherRef = obj; // obj가 anotherRef로 참조됨
obj = null; // obj는 null이 되었지만 anotherRef가 참조 중 -> GC 대상 아님
Garbage Collection
https://mangkyu.tistory.com/118
Reachable과 Unreachable
https://velog.io/@yarogono/Java%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0Garbage-Collector%EB%9E%80
Minor GC, Major GC
https://s-y-130.tistory.com/111