[Java] JVM GC 기본 동작 방식 이해하기
예전에 내가 JVM 메모리가 어떻게 관리되는지 포스팅을 했었다.
https://devkingdom.tistory.com/226
오늘은 Heap 영역의 메모리를 청소하는 Garbage Collector 에 대해 알아보려고한다.
먼저 GC가 무엇인지부터 말씀드리겠다.
GC는 JVM의 Heap 영역에서 사용하지 않는 객체를 없애주는 프로세스를 의미한다.
Heap 영역에는 다양한 객체들이 올라와 있는데 이 객체들 끼리는 서로 참조하여 사용된다. 이렇게 GC는 GC Root 에서 시작해서 각 객체끼리 유효한 참조가 있으면 Reachable 객체, 그렇지 않다면 Unreachable 객체라 판단한다.
여기서 객체는 힙내에 있는 다른 객체에 의한 참조가 있을수 있고,
java 스택, 즉 메서드 내의 지역변수나 파라미터들에 의한 참조가 있을수 있고,
JNI에 의해 생성된 객체에 대한 참조가 있을수 있고
메서드 영역의 Static 변수에 의한 참조가 있을 수 있다.
이렇게 시작된 데이터들이 GC Root가 될 수 있다.
아래 그림을 보면 무슨말인지 더 이해가 되실거라 본다.
여기서 GC의 청소 대상이 되는 것이 바로 Unreachable 이다.
GC는 기본적으로 Mark and Sweep 이라는 방식을 거친다.
Mark 는 GC Root 에서 시작해서 모든 변수를 싸악~~ 스캔해서 각각 어떤 객체를 참조하고 있는지 찾아서, 얘는 reachable, 얘는 unreachable 이렇게 마킹하는 과정이라고 생각하면된다.
그리고 Sweep 과정에서 unreachable 한 놈들을 날려버린다.
그리고 알고리즘에 따라 다르게 Compact라는 것도 동작을 한는데 sweep을 하고나면 군데 군데 뻥뻥뚤려서 객체들이 분산이 되어있을거다. 이때 heap의 시작주소로 쭉 모아서 메모리가 할당된 부분과 안된 부분으로 나누는 과정을 Compact라고 한다. 이 과정은 메모리 단편화를 방지한다.
다음은 GC 동작과정에 대해 설명을 드리겠다.
GC 동작과정을 보기전에 GC 설계자들의 2가지 가설을 잘 기억하자.
1. 대부분의 객체는 금방 접근 불가능한 상태가 된다.
2. 오래된 객체에서 젊은객체로의 참조는 아주 적게 존재한다.
일단 GC를 이해하기 위해서는 Heap 구조를 알아야하는데
Heap은 Young Generation 과 Old Generation 영역 그리고 Meta Space로 나뉜다.
Young Generation 은 새로운 객체들이 할당되는 영역을 의미하고,
Old Generation 은 오랫동안 계속 살아남은 객체들이 있는 영역을 의미한다.
그리고 Meta Space는 gc가 일어날때 필요한 클래스와 메소드의 메타정보가 있는 영역을 의미한다.
Young Generation 을 세부적으로 보면 Eden, Survivor 0 ,Survivor 1 영역이 있다.
여기서 새롭게 생성된 객체는 우선 Eden으로 들어온다.
여기서 Eden 이 꽉 차면 Minor GC 가 일어난다.
먼저 앞서 말씀드렸던 것처럼 Mark 과정이 일어나고
Reachable한 녀석들을 Survivor 0 영역으로 이동시킨 뒤,
Sweep으로 Unreachable한 녀석들을 싸악 날려버린다.
여기서 살아남은 객체들의 Age 값은 하나씩 증가한다.
그리고 또 다시 Eden 영역에 값이 할당 되고
또 Minor GC가 발생한다.
Mark 과정에선 Eden 영역 뿐만 아니라 다른 영역의 있는 값에 대해서도 Mark를 진행한다.
그러고 Reachable 한 놈들을 이번에는 Survivor 1로 옮긴다.
이렇게 반복되다보면 객체의 age값이 특정 임계점에 도달하게 되는데,
이때 이 객체들을 Old generation 으로 이동시킨다. (Promoted)
이렇게 진행되다가 Old Generation도 꽉 차게되면 여기서 Major GC (Full GC) 가 발생하게 된다.
GC 가 발생하게 되면 Gc 를 수행하는 스레드 이외에 JVM이 모든 스레드의 작업을 중단 시키는데 이걸
stop-the-world 라고한다. 뭔가 영화 제목 같지 않나 ㅎㅎ??
여기서 stop-the-world 가 발생하게 되면 어플리케이션이 잠깐 멈추는데 ... Minor GC 같은 경우에는 Stop the world 가 발생하면 잠깐 멈추고 말지만 Major GC는 소요되는 시간이 오래 걸려... 자칫 잘못하면 큰 에러로 이어지는 경우가 있으니 결국 Major gc 가 일어나지 않도록 할 수 밖에 없다. 근데 ... 결국 Major gc가 일어나지 않도록하는건 나는 개발자의 역량이라고 생각한다. 너무 길게 참조, 참조, 참조하는 그런 개발 행위는 ... 좀 버려야 하지 않을까 싶다.
GC 의 종류마다 stop the world 가 다 다르게 일어난다. GC의 종류는 아래와 같은데
- Serial GC
- Parallel GC
- Parallel Old GC
- CMS(Concurrent Mark Sweep) GC
- G1(Garbage First) GC
궁금하신 분들은 꼭 검색해서 공부해보시길 권한다.
오늘은 간단하게 GC를 정리했다.
끝.