가비지 및 가비지의 역할
유효하지 않은 메모리를 가바지(Garbage)라고 하며, C / C++의 경우는 메모리를 개발자가 할당 및 해제를 해줘야하는데
자바는 가비지 컬렉터(Garbage Collerctor)가 자동으로 불필요한 메모리를 정리해줍니다.
- > 자동으로 해줘서 장점이지만 동시에, 느리다는 단점이 있다고 합니다.
가비지 컬레션은 메모리 해제하는 전반적인 작업을 뜻한다면, 가비지 컬렉터는 JAVA에서의 해제 작업 담당을 말합니다.
가비지의 예시
String[] array = new String[2];
array[0] = "0"; array[1] = "1";
array = new String[] {'X', 'Y'};
위 코드에서 array = new String[] {'X', 'Y'} 3번째 라인 전에 할당된 0,1은 주소를 잃어버려서 사용할 수 없는 메모리가 됐습니다.
이와 같은 메모리를 GC가 잡아줘서 개발자 입장에서 편리한 기능
GC는 언제 실행될까요... ?
- JVM은 메모리를 부여받고 열심히 프로그램들을 실행하다가 메모리가 부족해지는 순간이 오면 os에게 추가로 메모리를 요청합니다. 이때 메모리를 더 달라고 요청할 때 GC가 실행됩니다. 서버 프로그램의 경우 24시간 돌아가는데, 이때 JVM이 한가할 때(idle time일 때) GC가 실행되고, JVM이 종료되면 당연히 사용하던 모든 메모리는 OS에 반환됩니다.
GC의 동작 방식
1. stop the world
2. Mark and Sweep 과 Reachability 과정이 있습니다.
stop the world
- > GC 실행을 위해 JVM이 애플리케이션 실행을 멈추는 것(suspend상태)
- GC가 실행될 때는, GC를 실행하는 스레드를 제외한 모든 스레드는 작업을 멈추고, GC작업이 끝나면 중단했던 작업을 다 시 시작한다.
- 대개 GC튜닝이란, 이 stop-the-world 시간을 줄이는 것을 의미
프로세스 쓰레드 설명 간략하게,,,
프로세스 = 운영체제에서 실행되는 하나의 프로그램 단위이다 (ex. 크롬, 한글, 엑셀)
쓰레드 = 프로세스내에서 실행되는 세부 작업단위
Mark and Sweep 과 Reachability
Mark and Sweep
- > GC과정의 기본 알고리즘이라고 보면 될 것 같다.
Mark
- GC가 스택의 모든 변수 혹은 Reachable 객체를 스캔하면서 각각 어떤 객체를 참조하고 있는지 찾는 과정
- 이 과정에서 stop-the-world가 발생
Sweep
- Mark되어있지 않은 객체들(= 가비지 객체)을 힙에서 제거하는 과정
- Mark가 끝나면 가비지 컬렉터는 힙 내부를 전체를 돌면서 Mark 되지 않은 메모리들을 해제한다.
Reachability
- > JAVA에서 GC가 가비지 객체를 판별하기 위해 사용하는 개념
어떤 객체에 유효한 참조가 있으면 reachable, 없으면 unreachable로 구별하는데 이 상태를 가비지로 간주해서 GC 수행한다. 한 객체는 여러 다른 객체를 참조하고, 참조된 다른 객체들도 마찬가지로 또다른 객체들을 참조할 수 있으므로 객체들은 참조 사슬을 이룬다. 유효한 참조 여부를 파악하기위해 항상 유효한 최초의 참조가 있어야하는데 이를 객체 참조의 root set이라고 한다.
1. 힙 영역 내의 다른 객체에 의한 참조
2. 스택 영역의 Java 메서드 내에서 실행하는 지역 변수, 파라미터, 연산 작업중 피연산자에 의한 참조
3. 메서드 영역의 상수 풀이나 정적 변수에 의한 참조
4. 아직 메모리에 남아 있는 Native 메서드로 넘겨진 객체에서 참조 (JNI에 의해 생성된 객체에 대한 참조)
메모리 영역 관련
https://beststar-1.tistory.com/14?category=975450
2,3,4,가 root set으로, reachability가 가능한 경우입니다.
unreachable객체들이 GC의 대상입니다.
세부적인 GC과정
GC는 두 가지 가설 하에 만들어졌다.
1. 대부분의 객체는 금방 접근 불가능 상태(unreachable)가 된다.
2. 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
이 가설의 장점을 최대한 살리기 위해 HotSpot VM에서는 크게 2개의 물리적 공간인 Young영역과 Old영역으로 나누었다.
Young 영역 : 새롭게 생성한 객체가 위치, 대부분의 객체가 금방 unreachable 상태가 되기 때문에 많은 객체가 Young 영역에 생성되었다가 사라진다.
Old 영역 : Young 영역에서 reachable 상태를 유지해 살아남은 객체가 여기로 복사된다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생한다.
GC 과정은 크게 Minor GC, Major GC, Full GC 3가지로 나뉩니다..
Minor GC : New / Young 영역에서 일어나는 GC
-> JVM에 새로운 객체가 위치할 공간이 충분하지 않으면, 공간을 확보하기 위해 GC 실행하는 것
1. 새로 생성한 대부분의 객체는 Eden영역에 위치하며, 이 영역이 가득차면 GC가 발생한다.
2. unreachable객체들은 Eden영역이 클리어 될 때 메모리에서 해제되며, reachable객체들은 Survivor영역중 하나로 이동된다. 이때 두 Survivor 공간 중 하나는 항상 비어있어야 한다.
3. Eden 영역에서 객체가 가득 찰 때마다, Eden영역과 사용중인 Survivor영역을 대상으로 GC가 발생한다. 그리고 두 영역의 reachable객체들은 빈 Survivor영역으로 이동되고, 다른 영역의 객체를 클리어한다.
4. 3번 과정을 반복하다가 계속해서 살아남아 있는 객체는 Old(Tenured)영역으로 이동하게 된다.
(기본적으로 15번의 GC Cycle을 기준으로 한다.)
Minor GC의 장점
1. Young Gen은 Old Gen보다 사이즈가 작고, GC가 전체 영역을 처리하는 것보다 덜 걸린다. 즉, stop-the-world로 애플리케이션이 중지되는 시간이 짧아진다.
2. Young Gen을 한 번에 모두 비우기 때문에 이 영역에 연속적인 공간이 만들어져 메모리 파편화를 방지 할 수 있다.
Major GC : Old 영역을 청소
Old Gen의 공간은 상당히 크며, 가비지가 될 가능성이 낮은 객체가 저장된다. 따라서 Young Gen에 비해 GC가 상대적으로 덜 발생하며, 시간이 오래 소요된다. 기본적으로 데이터가 데이터가 가득차면 GC를 실행되며, 대부분의 객체는 reachable하다고 예상하여 Mark와 Copy가 발생하지 않는다.
- Young Gen의 GC에 비해 상대적으로 시간이 오래 소요되기 때문에 Old Gen으로 이동하는 객체의 수를 줄이면 Full GC가 발생하는 빈도를 많이 줄일 수 있다.
Full GC : Young & Old 영역 모두에서 Heap 전체를 청소
실행시간이 상대적으로 Minor GC에 비해 길기 때문에, 실행에 오래 소요되면 연계된 여러 부분에서 타임아웃이 발생 할 수 있다.
하지만, FULL GC 실행시간을 줄이기 위해 Old Gen의 크기를 줄이면 OutMemoryError가 발생하거나 Full GC 횟수가 증가하며, 반대로 크기를 늘리면 Full GC 횟수는 줄어들지만 실행시간이 늘어난다.
그렇기에 핵심은 Old영역의 크기를 적절하게 설정하는 것이 중요 !!
- > GC튜닝 참고 사이트 https://d2.naver.com/helloworld/37111
ref
https://d2.naver.com/helloworld/329631
'Java 공부' 카테고리의 다른 글
자바의 데이터 타입(Primitive type, Reference type) (0) | 2022.02.28 |
---|---|
Java 컴파일 과정 (0) | 2022.02.28 |
동기화(Synchronized ) vs 비동기화(Asynchronized) / 블로킹(blocking)과 논블로킹(non-blocking) (0) | 2021.11.14 |
Java 객체 직렬화(Serialization) 와 역직렬화(Deserialization) (0) | 2021.11.14 |
자바 스레드(Thread) 정리글 (0) | 2021.11.14 |