GC에 대해서

갱두·2021년 9월 27일
0

📚 Java

목록 보기
2/9

Garbage Collection

자바에서는 개발자가 프로그램 코드로 메모리를 명시적으로 해제하지 않기 때문에 Garbage Collector가 더 이상 필요 없는 객체를 찾아 지우는 작업을 한다.

Garbage Collector는 두 가지 가설(=weak generational hypothesis) 하에 만들어짐

  • 대부분의 객체는 금방 접근 불가능한 상태(unreachable)가 된다.
  • 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
    이 가설의 장점을 최대한 살리기 위해 Young 영역과 Old 영역으로 물리적 공간을 나눔

✔️ Young 영역의 구성

Young 영역은 3개의 영역으로 나뉜다.

  • Eden 영역
  • Survivor 영역 2개

각 영역의 처리 절차를 순서에 따라서 기술하면 다음과 같다

  1. 새로 생성한 대부분의 객체는 Eden 영역에 위치한다.
  2. Eden 영역에서 GC가 한 번 발생한 후 살아남은 객체는 Survivor 영역 중 하나로 이동된다.
  3. Eden 영역에서 GC가 발생하면 이미 살아남은 객체가 존재하는 Survivor 영역으로 객체가 계속 쌓인다.
  4. 하나의 Survivor 영역이 가득 차게 되면 그 중에서 살아남은 객체를 다른 Survivor 영역으로 이동한다. 그리고 가득 찬 Survivor 영역은 아무 데이터도 없는 상태로 된다.
  5. 이 과정을 반복하다가 계속해서 살아남아 있는 객체는 Old 영역으로 이동하게 된다.

Survivor 영역 중 하나는 반드시 비어 있는 상태로 남아 있어야 한다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나, 두 영역 모두 사용량이 0이라면 시스템이 정상적인 상황이 아니라고 보면 됨.

🔥 Eden 영역에 최초로 객체가 만들어지고, Survivor 영역을 통해서 Old 영역으로 오래 살아남은 객체가 이동한다는 사실은 꼭 기억하기 🔥

✔️ Old 영역

Old 영역은 기본적으로 데이터가 가득 차면 GC를 실행함.
GC 방식에 따라서 처리 절차가 달라짐. 그건 밑에서 언급함

✔️ Permanent 영역

= Method Area
객체나 억류된 문자열 정보를 저장하는 곳이며 Old 영역에서 살아남은 객체가 영원히 남아 있는 곳은 아니다. 이 영역에서 GC가 발생할 수도 있는데 여기서 GC가 발생해도 Major GC의 횟수에 포함됨

1. Minor GC

매우 많은 객체가 Young 영역에 생성되었다가 사라지고 이 영역에서 객체가 사라질 때 Minor GC가 발생한다고 말함.
대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라진다.

2. Major GC

Old 영역에 있는 모든 객체들을 검사하여 참조되지 않은 객체들을 한꺼번에 삭제한다. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다는 GC가 적게 발생함. 시간이 오래 걸리며 실행 중 프로세스가 정지된다.
🔥 Stop-the-world 🔥
GC를 실행하기 위해 JVM이 어플리케이션 실행을 멈추는 것
stop-the-world가 발생하면 GC를 실행하는 스레드를 제외한 나머지 스레드는 모두 작업을 멈춘다. GC 작업을 완료한 이후에나 중단했던 작업을 다시 시작함
어떤 GC 알고리즘을 사용하더라도 stop-the-world는 발생하며, 대개 GC 튜닝이란 stop-the-world 시간을 줄이는 것을 말함

🤷🏻‍♀️ Old 영역에 있는 객체가 Young 영역의 객체를 참조하는 경우에는 어떡하는가?

이러한 경우를 처리하기 위해 Old 영역에는 512바이트의 덩어리로 되어 있는 카드 테이블이 존재한다.

카드 테이블에는 old 영역에 있는 객체가 young 영역의 객체를 참조할 때마다 정보가 표시된다. Young 영역의 GC를 실행할 때는 Old 영역에 있는 모든 객체의 참조를 확인하지 않고, 이 카드 테이블만 뒤져서 GC 대상인지 식별한다.
카드 테이블은 Minor GC를 빠르게 할 수 있도록 하는 장치인 write barrier를 사용하여 관리함. write barrier 때문에 약간의 오버헤드는 발생하지만 전반적인 GC 시간은 줄어든다.

🤷🏻‍♀️ GC는 어떤 원리로 소멸시킬 대상을 선정하는가?

알고리즘에 따라 동작 방식이 매우 다양하지만 공통적인 원리가 있음
Garbage Collector는 힙 내의 객체 중에서 Garbage를 찾아내고 찾아낸 Garbage를 처리해서 힙의 메모리를 회수함
✔️ 참조되고 있지 않은 객체 = Garbage
✔️ 객체가 Garbage인지 아닌지 판단하는 개념 = Reachability
어떤 힙 영역에 할당된 객체가 유효한 참조가 ⭕️ : Reachability / ❌ : Unreachability
✔️ 하나의 객체는 다른 객체를 참조하고, 다른 객체는 또 다른 객체를 참조할 수 있기 때문에 참조 사슬이 형성 , 최초로 참조한 것을 Root Set

💡 힙 영역에 있는 객체들은 총 4가지 경우에 참조를 하게 됨
1. 힙 내의 다른 객체에 의한 참조
2. 자바 스택, 즉 자바 메소드 실행 시에 사용하는 지역변수와 파라미터들에 의한 참조
3. 네이티브 스택에 의해 생성된 객체에 대한 참조
4. 메소드 영역의 정적 변수에 의한 참조

-> 2,3,4번은 Root Set임. 즉, 참조 사슬 중 최초로 참조한 것

인스턴스가 GC의 대상이 되었다고 해서 바로 소멸이 되는 것 ❌

빈번한 GC의 실행은 시스템에 부담이 될 수 이씩에 성능에 영향을 미치지 않도록 GC 실행 타이밍은 별도의 알고리즘을 기반으로 계산하고 이 계산결과를 기반으로 GC 수행

JDK 7을 기준으로 다섯가지가 있음
1. Serial GC
적은 메모리와 CPU 코어 개수가 적을 때(1개) 적합한 방식. 운영 서버에서는 절대 사용 ❌
Mark-Sweep-Compact 알고리즘을 사용함
Mark : Old 영역에 살아 있는 객체를 식별 -> Sweep : 힙의 앞 부분부터 확인하여 살아 있는 것만 남김 -> Compaction : 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눔

2. Parallel GC
기본적인 GC 알고리즘은 Serial GC와 동일하지만 Parallel GC는 GC를 처리하는 스레드가 여러 개라서 보다 빠르게 GC 수행 가능. 메모리가 충분하고 코어의 개수가 많을 때 유리

3. Parallel Old GC
JDK 5 update 6부터 제공한 GC방식
Parallel GC와 비교하여 Old 영역의 GC 알고리즘만 다름
이 방식은 Mark-Summary-Compaction 알고리즘을 사용함
✔️ Mark-Sweep-Compact와 차이점 : GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Sweep 단계와 다르고, 약간 더 복잡한 단계를 거침

4. Concurrent Mark & Sweep GC

  • Initial Mark 단계 : 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾음 -> 멈추는 시간이 매우 짧다
  • Concurrent Mark 단계 : 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인함 이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다는 점
  • Remark 단계 : Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인
  • Concurrent Sweep 단계 : 쓰레기를 정리 이 단계도 다른 스레드가 실행 중인 상태에서 동시에 진행된다.

✔️ 특징 : stop-the-world 시간이 매우 짧음, 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용함
✔️ 단점 : 다른 GC 보다 메모리와 CPU를 더 많이 사용한다. Compaction 단계가 기본적으로 제공되지 않는다

따라서, CMS GC를 사용할 때는 신중히 검토한 후에 사용해야 한다. 조각난 메모리가 많아 Compaction 작업을 실행하면 다른 GC 방식의 stop-the-world 시간보다 stop-the-world 시간이 길기 때문에 compaction 작업이 얼마나 자주, 오랫동안 수행되는지 확인해야 함

5. G1(Garbage First) GC
지금까지의 Young 영역과 Old 영역에 대해서는 잊는게 좋다 🤬

G1 GC는 바둑판의 각 영역에 객체를 할당하고 GC를 실행한다. 그러다가 해당 영역이 꽉 차면 다른 영역에서 객체를 할당하고 GC를 실행한다.
즉, Young의 세가지 영역에서 데이터가 Old 영역으로 이동하는 단계가 사라진 GC 방식이라고 이해하면 된다.

✔️ 장점 : 성능
✔️ 하지만 JDK 6에서는 그냥 시험삼아 사용할 수만 있도록 한다. JDK 7에서 정식으로 제공함

출처 : https://asfirstalways.tistory.com/159
https://d2.naver.com/helloworld/1329

profile
👩🏻‍💻🔥

0개의 댓글