개발을 하다보면 유효하지 않은 메모리인 Garbage가 발생하게 됩니다. C언어와 같은 경우 free()라는 함수를 통해 직접 메모리를 해제 해주어야 하는 반면, JAVA를 사용하여 개발할 경우 개발자가 메모리를 직접 해제해주는 일은 거의 없는데요,
그 이유는 JVM의 가비지 컬렉터가 불필요한 메모리를 알아서 정리해주기 때문이죠.
예를 들어 이런 겁니다.
Tako tako = new Tako();
tako.setFlavor("sweet");
tako = null;
// 가비지
tako = new Tako();
tako.setFlavor("hot");
sweet이라는 tako객체는 참조되지 않고 사용이 되지 않아서 가비지가 되었습니다. Java는 이러한 메모리의 낭비를 방지하기 위해 Garbage Collector가 주기적으로 검사하여 메모리를 청소해주죠.
JVM의 heap영역은 처음 설계될 때 아래 2가지를 전제하고 설계되었다고 합니다.
1. 대부분의 객체는 금방 접근 불가능(Unreachable)한 상태가 된다.
2. 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.
즉 객체는 대부분 일회적이며, 메모리에 장기적으로 남아있는 경우는 드뭅니다.
그렇기 때문에 객체의 생존 기간에 따라 물리적인 heap영역을 나누게 되었는데
이에 따라 young, old 총 2가지 영역으로 heap영역이 나뉘게 되었습니다.
Young 영역
Young영역은 새롭게 생성된 객체가 할당되는 영역으로, 대부분의 객체가 접근 불가능(Unreachable)한 상태로 되기 때문에, 이러한 객체들은 Young영역에 있다가 사라집니다.
이 영역에 대한 가비지 컬렉션을 Minor GC라고 부르게 됩니다.
Old 영역
Young영역에서 접근 가능한 상태를 유지하며 살아남은 객체가 복사되는 영역으로
복사되는 과정에서 대부분 Young영역보다 크게 할당되며, 크기가 큰 만큼 가비지 영역은 적게 발생한다고 합니다. 이 영역에 대한 가비지 컬렉션을 Major GC또는 Full GC라고 부르게 됩니다.
GC의 동작방식
1. Stop the world
Stop the world는 가비지 컬렉션을 실행하기 위해 JVM이 애플리케이션의 실행을 멈추는 작업입니다.
GC가 실행될 때는 GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되고, GC가 완료되면 다시 작업이 시작됩니다.
2. Mark and Sweep
여기서 Mark는 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업이고 Sweep은 Mark단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업입니다.
Stop the world를 통해 GC를 실행하는 스레드를 제외한 모든 스레드를 중단시키면, GC는 스택 영역의 변수들과 Reachable 객체를 스캔하며,이들이 어떤 객체들을 참고하는지 탐색하게 됩니다. 사용되고 있는 메모리를 탐색하는데 이러한 과정을 Mark라고 하며, 후에 Mark되지 않은 객체들을 메모리에서 제거하는데 이를 Sweep이라고 합니다.
Minor GC의 동작 방식
Minor GC가 실행되는 Young영역은 1개의 Eden영역과 2개의 Survivor영역으로 나뉘어져 있습니다.
Eden영역은 새로 생성된 객체가 할당되며, 이 Eden영역이 꽉 찰 경우 Minor GC가 발생하게 됩니다. 이때 사용되지 않은 메모리는 해제되고 Eden영역에 존재하는 객체는 Survivor영역으로 복사되어 옮겨지게 됩니다. 다만 Survivor영역은 반드시 1개의 영역은 꼭 비어있는 상태여야 합니다. 정리하자면 다음과 같습니다.
1. 새로 생성된 객체가 Eden영역에 할당됩니다.
2. 객체가 계속 생성되어 Eden영역에 더이상 들어갈 자리가 없게 되고 Minor GC가 실행됩니다. 이때 Eden영역에서 사용되지 않는 객체의 메모리가 해제됩니다. Eden영역에서 살아남은 객체는 Survivor영역으로 이동됩니다.
3. 1에서부터 2번 과정이 반복되다가 하나의 Survivor영역이 가득 차게 되면 Survivor영역의 살아남은 객체를 다른 Survivor영역으로 이동시키게 됩니다. 이때 나머지 하나의 Survivor영역은 반드시 빈 상태여야만 합니다.
이 1~3과정을 반복하며 살아남은 객체는 Old영역으로 이동됩니다.
이 Old영역으로 이동할 객체들을 식별하기 위해 Minor GC에서 객체가 살아남은 횟수를 의미하는 age를 Object Header에 기록합니다.
Major GC의 동작방식
Young영역에서 오래 살아남은 객체는 Old영역으로 옮겨지게 됩니다. 이때 Major GC는 객체들이 계속 옮겨지게 되어서 Old영역의 메모리가 부족해지면 발생하게 되는데요, Old 영역은 Young영역보다 크기 때문에 일반적으로 Minor GC보다 Major GC의 수행시간이 더 길며, 10배정도의 시간을 더 사용한다고 합니다.
참고하면 좋은 글 )
https://d2.naver.com/helloworld/1329
'Language > Java' 카테고리의 다른 글
오버라이딩과 오버로딩 (0) | 2021.07.11 |
---|---|
어노테이션 (0) | 2021.07.03 |
JVM의 구조와 자바의 구동 원리 (2) | 2021.06.16 |
클래스 다이어그램 (0) | 2021.02.05 |
접근 제한자 4가지 (0) | 2021.02.04 |