📚 Heap 의 구성

https://user-images.githubusercontent.com/39696812/145256480-73080f15-c9e4-4311-997a-bad94d4ebc4a.png

Young Generation

인스턴스를 처음 생성했을 때 메모리에 배정되는 영역. 이곳에서 일어나는 GC를 Minor GC 라고 함

Eden

  • 객체가 최초로 할당 되는 곳
  • Eden 이 가득 찬 경우 객체들의 참조 여부를 확인하고, 참조가 없어진 경우 삭제 / 참조가 있는 경우 Survivor 로 넘긴다

Survivor

  • Survivor 0 과 Survivor 1 으로 구성
  • 살아 있는 object 들은 Survivor 0 이나 Survivor 1 중 하나만 사용함

Old Generation

Minor GC 를 여러번 거치고도 살아남은 인스턴가 배정되는 영역. 이곳에서 일어나는 GC 를 Major GC 라고 함

Old

  • Survivor 영역에서 살아남아 오랫동안 참조 되었고 앞으로도 사용될 확률이 높은 Object들을 저장하는 영역

Permanent Generation

Permanent

  • Java 8 이후 Metaspace 로 변경
  • Permanent 영역은 힙 공간이 아니고 OS 에서 관리함

Minor GC vs Major GC

자바를 처음 만든 사람들은 두가지 가설을 바탕으로 GC 를 설계

  1. Most allocated objects are not referenced for long, that is, they die young.

    대부분의 객체는 금방 접근 불가능한 상태가 된다. 즉, 금방 garbage 가 된다

  2. Few references from older to younger objects exist.

    오래된 객체에서 젊은 객체로의 참조는 아주 적게 일어난다

https://user-images.githubusercontent.com/39696812/145441613-87403f02-fb3e-41ad-81d5-b42ccb1f78f8.png

그렇기 때문에 자주 객체가 UnReachable 상태가 되는 Young Reference 영역을 구분해 주기적으로 GC 가 발생하고,

🔩 GC 사용 알고리즘 종류

1. Reference Counting

https://user-images.githubusercontent.com/59424640/145065130-2d21b9e1-bb77-459c-a500-1e80da860886.png

  • Heap 에 선언된 객체들이 해당 객체에 몇가지 방법으로 접근 할 수 있는지에 대한 숫자를 가지고 있는 Reference count 를 가지고 있고, 이 숫자가 0이 되는 경우 참조하는 변수가 없다고 생각하고 해당 레퍼런스를 삭제

  • 하지만 위의 노란색 부분 처럼 레퍼런스가 계속해서 참조되는 순환참조가 발생할 수 있음

2. Mark-Sweep-Compaction

https://mirinae312.github.io/img/jvm_gc/MarkSweepCompaction.png

  • GC가 사용되지 않는 객체를 메모리에서 제거하는 과정인만큼, GC 대상객체를 식별하고 제거하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업을 수행

Mark :: 사용되지 않는 객체를 식별하는 작업

  • GC 가 Stack 의 모든 변수를 스캔하면서 각각 어떤 객체를 참조 하고 있는지 찾아서 마킹
  • Reachable Object 가 참조하고 있는 객체도 찾아서 마킹

Sweep :: 사용되지 않는 객체를 제거하는 작업

  • 마킹되지 않은 객체(Unreachable)를 Heap 에서 제거

Compaction :: 파편화된 메모리 영역을 앞에서부터 채워나가는 작업

  • Mark & Sweep 방식으로 살아남은 객체를 앞에서부터 채어나가도록 정리

🏋️‍♀️ GC 가 일어나는 과정

Reachable 객체 vs Unreachable 객체

https://user-images.githubusercontent.com/59424640/145066471-b2761acb-87e1-4bc4-b6f3-42d52bc463c8.png

GC Roots : 참조하는 가장 상위 값이며, 밑의 내용들이 될 수 있음

  • Stack 의 로컬 변수
  • Method Area 의 Static 변수
  • Native Method Stack 의 JNI 참조

Reachable Object : GC Roots 에서 참조하는 객체들

Unreachable Object : GC Roots 에서 참조하지 않는 객체들

과정

  1. 새로운 객체는 Eden 영역에 할당된다
  2. Eden 영역이 모두 사용된 경우 GC 발생 (Minor)
  3. Eden 의 Reachable 객체는 Survival 0 으로 옮겨지고, Unreachable 객체는 Eden 에서 사라진다.

Survival 0 가 가득 찬 경우

  1. Mark & Sweep 과정이 일어나고 Reachable 객체는 Survival 1 으로 옮겨진다
  2. 이동한 객체는 Age 값이 증가됨

Survival 0, 1 중 차있는 영역에 계속 쌓이고, survival 0,1 중 하나는 반드시 비어있게 됨

계속 Minor GC 를 반복하다 Age 값이 특정 값 이상을 넘어서면 Old Generation 영역으로 옮겨진다 => promotion

자바 8 의 Parallel GC 방식 사용 기준 15가 되면 promotion 이 진행

⛔️ Stop The World

GC 를 실행하기 위해 JVM 이 어플리케이션 실행을 멈추는 것

➡️ GC 를 실행하는 스레드 외 다른 스레드는 모두 동작을 멈춤

➡️ stop-the-world 시간을 최소화 해야함

🔄 GC 종류와 변화

Serial GC

  • -XX:+UseSerialGC

  • 가장 단순한 방식의 GC로 싱글 스레드(스레드 1개)로 동작한다.

  • 싱글 스레드로 동작하여 느리고, 그만큼 Stop The World 시간이 다른 GC에 비해 길다.

  • Mark & Sweep & Compact 알고리즘을 사용

  • 보통 실무에서 사용하는 경우는 없음 (디바이스 성능이 안좋아서 CPU 코어가 1개인 경우에만 사용)

https://blog.kakaocdn.net/dn/VKJKl/btqZoyLxsKB/yelw9JtXxtRXUw8jABsjYK/img.png

Parallel GC

  • -XX:+UseParallelGC

  • Java 8의 default GC

  • Young 영역의 GC를 멀티 스레드 방식을 사용하기 때문에, Serial GC에 비해 상대적으로 Stop The World 가 짧다

https://blog.kakaocdn.net/dn/Lt8SI/btqZpUghb6U/bWL987lUHZTGe51qHfoOVk/img.png

Parallel Old GC

  • -XX:+UseParallelOldGC / -XX:+ParallelGCThreads=n

  • Parallel GC는 Young 영역에 대해서만 멀티 스레드 방식을 사용했다면, Parallel Old GCOld 영역까지 멀티스레드 방식을 사용

  • -XX:+ParallelGCThreads=n 옵션으로 멀티 스레드 개수를 지정할 수 있음

CMS GC (Concurrent Mark Sweep GC)

Stop The World로 Java Application이 멈추는 현상을 줄이고자 만든 GC

Reacable 한 객체를 한번에 찾지 않고 나눠서 찾는 방식을 사용 (4 STEP으로 나눠짐)

https://blog.kakaocdn.net/dn/chM1aO/btqZtAafO9I/x5ieeSoLcjfbMftBS6pkLK/img.png

  • Initial Mark: GC Root가 참조하는 객체만 마킹 (stop-the-world 발생)
  • Concurrent Mark: 참조하는 객체를 따라가며, 지속적으로 마킹. (stop-the-world 없이 이루어짐)
  • Remark: concurrent mark 과정에서 변경된 사항이 없는지 다시 한번 마킹하며 확정하는 과정. (stop-the-world 발생)
  • Concurrent Sweep: 접근할 수 없는 객체를 제거하는 과정 (stop-the-world 없이 이루어짐)

위와 같이 stop-the-world가 최대한 덜 발생하도록 하여, Java Application이 멈추는 현상을 줄임

G1 GC

Java 9+ 의 default GC

현재 GC 중 stop-the-world의 시간이 제일 짧음

CMS GC 를 개선하여 만든 GC로 위에서 살펴본 GC와는 다른 구조를 가진다. (아래 그림 참고)

https://blog.kakaocdn.net/dn/bfoIkH/btqZyv7jRen/KvssCKeoo9rq56Uh3N1P60/img.png

Heap을 Region이라는 일정한 부분으로 나눠서 메모리를 관리한다.

전체 Heap에 대해서 탐색하지 않고 부분적으로 Region 단위로 탐색하여, 각각의 Region에만 GC가 발생한다.

🙇🏻‍♀️ 레퍼런스

[10분 테코톡] 🐥엘리의 GC

[10분 테코톡] 🤔 조엘의 GC

[10분 테코톡] 👌던의 JVM의 Garbage Collector

Java 의 GC는 어떻게 동작하나?

JVM GC 동작 순서와 GC 종류(Serial / Parallel / CMS / G1 GC )