// 에드센스

프로그래밍을 하다보면 사용하지 않는 일명 "쓰레기"공간이 발생하여 프로그램의 성능을 저하시킨다. 자바에선 가비지 컬렉터가 이를 자동으로 탐지하여 해결해주는데 이 일을 해주는 가비지 컬렉터에 대해 알아보자. (이하 GC라고 하겠다.)

 

 

GC란

Person p1 = new Person("Kim");
Person p2 = new Person("Lee");

// p2가 가리키던 객체는 가비지가 된다.
p2 = p1;

 

 

Kim이라는 이름을 가진 p1 객체와 Lee라는 이름을 가진 p2객체가 있는데 p2라는 참조변수가 p1객체를 가리키게 한다면 원래 p2가 가리키던 Lee라는 객체는 더 이상 참조받을 수 없다. 즉 unreachable object가 되며 이를 가비지라고 한다. 

 

가비지 컬렉션이란

JVM의 힙영역에서 사용하지 않는 객체를 삭제하는 프로세스를 말한다.

 

 

GC는 Mark and sweep 알고리즘을 통해 동작한다.

  • mark는 reachable한 객체와 unreachable한 객체를 식별하는 과정
  • sweep은 식별한 unreachable객체를 제거하는 과정
  • compact과정도 추가되기도 한다. (메모리 단편화를 방지)

 

 

언제 동작하는가

GC도 결국엔 JVM에 올라가기 때문에 기본적으로 런타임에 동작한다.

가비지 컬렉션이 실행되기에는 몇 가지 조건이 있는데, 다음 조건 중 하나라도 충족되면 JVM은 GC를 실행한다.

 

  • OS로부터 할당 받은 시스템의 메모리가 부족한 경우
  • 관리하고 있는 힙에서 사용되는 메모리가 허용된 임계값을 초과하는 경우
  • 프로그래머가 직접 GC를 실행하는 경우(Java에서는 System.gc()라는 메소드가 있지만 가급적 안 쓰는 것이 좋다.)

 

 

JVM의 Heap 메모리 구조

이렇게 생겼다. 여기서 우리는 오른쪽의 Permanent영역을 제외한 부분만 살펴보자. 참고로 Young 영역에서 발생하는 GC를 Minor GC, Old영역의 GC는 Major GC라고 한다. Minor GC와 Major GC를 따로 만든 이유는 대부분의 객체는 금방 가비지가 된다는 가설을 전제로 하고 GC를 설계했기 때문이다.

Minor GC의 범위에서 사용되는 객체들(파란영역)이 훨씬 많은것을 알 수 있다.

 

Young Generation

GC를 이해하기 위해서 객체가 제일 먼저 생성되는 Young 영역부터 알아보자. Young 영역은 3개의 영역으로 나뉜다.

  • Eden 영역
  • Survivor 영역(2개)

Survivor 영역이 2개이기 때문에 총 3개의 영역으로 나뉘는 것이다. 각 영역의 처리 절차를 순서에 따라서 기술하면 다음과 같다.

  • 새로 생성한 대부분의 객체는 Eden 영역에 위치한다.
  • Eden영역이 꽉 차면 GC(Minor GC)가 발생한다.
  • Eden 영역에서 GC(Minor GC)가 한 번 발생한 후 살아남은 객체는 Survivor 영역 중 하나로 이동된다.
  • 이때 Survivor영역은 둘 중 한쪽만 사용돼야 한다.
  • 그렇기에 Minor GC가 발생할때 마다 두 군데의 Survivor 영역을 이동하며 저장된다.
  • GC가 발생할때마다 살아남은 객체들은 Age가 증가한다.
  • 일정 Age에 도달한 객체들은 Old 영역으로 이동하게 된다.

이 절차를 확인해 보면 알겠지만 Survivor 영역 중 하나는 반드시 비어 있는 상태로 남아 있어야 한다. 만약 두 Survivor 영역에 모두 데이터가 존재하거나, 두 영역 모두 사용량이 0이라면 여러분의 시스템은 정상적인 상황이 아니라고 생각하면 된다.

 

 

Old Generation

Young 영역에서 오랫동안 살아남은 객체들이 넘어오는 곳이다. 이곳 역시 꽉차면 Major GC의 과정이 수행된다. 주로 5가지의 GC방식이 존재한다.

  • Serial GC
  • Parallel GC
  • Parallel Old GC(Parallel Compacting GC)
  • Concurrent Mark & Sweep GC
  • G1(Garbage First) GC

 

*stop-the-world란 GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는것

 

Serial GC

  • GC를 처리하는 쓰레드가 1개(싱글 쓰레드)
  • 다른 GC에 비해 stop-the-world 시간이 길다
  • Mark-Compact 알고리즘 사용

 

Parallel GC

  • Java8의 default GC
  • Young 영역의 GC를 멀티쓰레드로 수행
  • 그렇기에 Serial GC에 비해 stop-the-world 시간이 짧다

 

Parallel Old GC

  • Parallel GC를 개선
  • Old영역에서도 멀티쓰레드로 GC 수행
  • Mark-Summary-Compact 알고리즘 사용

 

CMS GC

  • stop-the-world 시간을 줄이기 위해 고안됨
  • compact과정이 없음

 

G1 GC

  • CMS GC를 개선
  • Java9의 default GC
  • Heap영역을 일정한 크기의 Region으로 나눔
  • 전체 Heap이 아닌 Region단위로 탐색
  • Compact 진행

 

 

 

 

참고:

더보기

'Java' 카테고리의 다른 글

[Java] 해시/해시테이블이란?  (0) 2021.07.12
[Java] 제네릭(Generic)  (0) 2021.07.11
[Java] Thread/MultiThread 4 - 동시성 문제  (0) 2021.06.29
[Java] Static 키워드  (0) 2021.06.28
[Java] JVM 구조와 동작원리  (0) 2021.06.27

+ Recent posts