본문 바로가기

CS

[JAVA] - GC(Garbage Collection) 가비지 컬렉션 정리

가비지 컬렉션(Garbage Collection)이란 ?

자바의 메모리 관리 기법으로 동적으로 할당했던 메모리 영역(=heap) 중 더 이상 참조되지 않는 메모리 객체를 모아 제거 해주는 객체.

-> Heap Area에서 더이상 사용하지 않는 메모리를 제거

Heap Area
Heap Area
Eden survivor 1 survivor 2 Old Generation Permanent
Young Generation  
※ Permanent 는 Java 7까지만 Heap에 존재. Java 8부터는 Native Method Stack으로 편입.

Stack vs Heap

Stack :

  • 정적으로 할당한 메모리 영역
  • 원시 타입의 데이터(int, double, boolean 등... )가 값과 함께 할당. 
  • Heap영역에 생성된 Object 타입의 데이터의 참조 값 할당

 

Heap :

  • 동적으로 할당한 메모리 영역
  • 모든 Object 타입의 데이터(참조 타입, String, Class, Interface 등...)가 할당
  • Heap영역의 Object를 가리키는 참조 변수가 Statck에 할당

가비지 컬렉션의 종류

  1. Serial GC
  2. Paraller GC
  3. CMS GC
  4. G1 GC
  5. Z GC

Serial GC

  • Mark-and-Compact 알고리즘 사용
  • 하나의 CPU로 Young Generation / Old Generation을 연속적으로 처리
  • 가장 오래된 GC
  • GC가 수행될 때 Stop the World 발생

Parallel GC (자바 7, 8)

  • 자바 7~8 에서 Default로 설정되어 있는 GC
  • 다른 CPU가 GC의 진행시간 동안 대기 상태로 남아 있는 것을 최소화
  • GC 작업을 병렬로 처리 ➡️ stop the world 시간이 비교적 짧음

Parallel Compacting GC

Parallel GC에서 Old Generation의 처리 알고리즘을 변경

Concurrent Mark-Sweep(CMS) GC

Application의 Thread와 GC Thread가 동시 실행 ➡️ STW를 최소화

Parallel GC와 가장 큰 차이점 = Compaction 작업 유무

Garbage First(G1) GC 

큰 메모리에서 사용하기 적합한 GC (대규모 Heap 사이즈에서 짧은 GC 시간을 보장하는 데 목적)

전체 Heap 영역을 Region이라는 영역으로 분할 ➡️ 상황에 따라 역할이 동적으로 부여

(※ Region : Eden / S1 / S2 / Old Generation 등이 상황에 따라 동적으로 ... ) 

Z GC

ZPage라는 영역을 사용

G1 GC의 Region은 크기가 고정 ➡️ ZPage는 2mb 배수로 동적으로 운영

정지 시간이 최대 10ms를 초과하지 않는 것을 목적으로 운영

Heap 크기가 증가해도 정지 시간은 증가하지 않음

가비지 컬렉션의 활동 영역 

Heap 메모리 영역에서 활동.

Stop The World

  • 가비지 컬렉션을 효율적으로 사용하기 위해 잠시 JVM이 멈추는 현상.
    (Old Generation에 쌓인 많은 객체를 효율적으로 제거하기 위해 잠시 멈춤)
  • GC가 작동하는 동안 GC관련 쓰레드를 제외한 다른 쓰레드는 멈춤.
  • 일반적으로 "튜닝"을 한다는 것은 이 멈추는 시간을 최소화하는 작업을 말함
    • 튜닝하지 않으면 보통은 Parallel GC 가 기본탑재
      보통 튜닝하면 Concurrent Mark-Sweep(CMS) GC 혹은 Garbage First(G1) GC를 많이 사용한다.

사용방법 

System.gc() 함수로 실행할 수 있지만, 실행을 보장하지 않으며, 성능 저하를 일으킬 수 있음.

(일반적으로 자동으로 실행)

가비지 컬렉션의 위치

Execution Engine에 위치

( Execution Engine = Interpreter + JIT Compiler + Garbage Collection)

  • Interpreter: 바이트 코드를 해석, 실행 (같은 메소드를 여러번 호출 시, 매번 새로 수행)
  • JIT(Just In Time) Compiler: Interpreter의 단점을 보완. 반복되는 코드를 발견 후, 전체 바이트 코드를 컴바일하여 Native Code(C or C++ or assembly)로 변환하여 사용

가비지 컬렉션의 기반 가설

"약한 세대 가설(Weak Generational Hypothesis)"을 기반으로 메모리 구조를 Young Generation / Old Generation 으로 나눔.

 

약한 세대 가설(Weak Generation Hypothesis)
1. Unreachable, 대부분의 객체는 금방 접근 불가능한 상태가 된다.
 대부분의 객체는 중괄호{} 안에서 생성
 이 객체들은 괄호가 끝나는 시점에서 더이상 사용되지 않음
 대부분의 경우 Unreachable 상태가 되어 GC의 대상이 됨.

2. 오래된 객체에서 젊은 객체로의 참조는 아주 적게 발생
 일반적으로 순차적인 로직에 의해 객체를 생성하여 활용
 이 과정에서 앞에 생성된 객체는 그 다음의 로직에서 사용된 후 대부분 사용하지 않음
 이런 특성으로 더 이상 참조되지 않는 객체에 대해 GC를 할 수 있게됨.

> 요약 <
1. 대부분의 객체는 {중괄호}가 끝나는 시점부터 사용 X
2. 이전 로직에 생성된 객체는 다음 로직에서 사용 X 

Unreachable Object 설명

https://www.youtube.com/watch?v=vZRmCbl871I 2분 10초 참고

pulic class Main{
	public static void main(String[] args) {
    	int num1 = 10;
        int num2 = 5;
        int sum = num1 + num2;
        
        String name = "A"
        
        Sout(sum);
        Sout(name);
    }
}

위의 코드를 실행 시 Stack과 Heap의 모습)

Stack

...
...
name
sum = 15
num2 = 5
num1 = 10

Heap

 
String = "A" (stack의 name)
 

위의 코드 실행 완료 후 Stack과 Heap의 모습

Stack (각 데이터들이 Pop() 되며 나감)

...
...
...
...
...
...

Heap

 
String = "A" (stack의 name)
 

코드의 실행 완료 후, Heap 영역에만 데이터가 남아있다. 이 데이터를 Unreachable Object라고 한다.

가비지 컬렉션의 알고리즘

GC 알고리즘의 기본 로직

  1. Garbage 대상을 식별
  2. 식별한 대상을 제거

GC 알고리즘 종류

  1. Reference Counting Algorithm
  2. Mark-and-Sweep Algorithm (RCA의 순환참조구조 극복불가 문제를 해결하기 위해 등장 ➡️ Garbage Mark)
  3. Mark-and-Compact Algorithm (MSA의 Out of Memory를 해결하기 위해 등장 ➡️ Compact 작업)
  4. Copying Algorithm
  5. Concurrent Mark-Sweep
  6. Generational Algorithm

Reference Counting Algorithm

  • Garbage 탐색에 초점을 맞춘 알고리즘
  • 객체마다 Reference Count를 관리하며, RC가 0이 되면 GC를 수행
장점 : 카운트가 0이 되면 바로 메모리에서 제거
단점 : 순환 참조 구조(Reference Cycle)에서 Reference Count가 0이 되지 않음 ➡️ Memory Leak

Mark-and-Sweep Algorithm

Reference Counting Algorithm의 단점을 극복하기 위해 등장한 알고리즘

RSet(Root Set)에서 Reference를 추적해 참조 상황을 파악

  1. Mark 단계 : Garbage 대상이 아닌 객체를 마킹하는 작업
  2. Sweep 단계 : 마킹되지 않은 객체를 지우는 작업
  3. 이후, 마킹 정보 초기화
장점 :
Reference Counting Algorithm의 순환 참조 구조 문제를 해결

단점 
:
1.GC가 동작하고 있을 경우, Mark 작업과 어플리케이션 Thread의 충돌을 피하기 위해 Heap 사용이 제한
2. Compaction 작업이 없어 비어 있는 공간이 충분하지 않을 경우 Out of Memory가 발생
※ Compaction : 디스크 조각 모음 작업

Mark-and-Compact Algorithm

Mark-and-Sweep에서 발생하는 점유 메모리 분산(Fragmentation)을 해결하기 위해 등장

Mark-and-Sweep 알고리즘의 Sweep 단계 이후 Compact 작업을 추가해 흩어져 있는 메모리를 모아서 정리

장점 :
Compact 작업으로 인해, 메모리 효율이 높아짐

단점 
:
Compact 작업과 그 이후 Reference를 업데이트 하는 작업으로 인해 오버헤드 발생할 수 있음

일반적인 GC과정 (Generational Algorithm)

일반적이고, 보편적인 GC 알고리즘

Heap의 각 영역을 활용하여 최적의 메모리 운영

  1. 맨 처음 객체 생성 시, Eden 영역에 생성
  2. GC의 일종인 Minor GC가 발생
    ➡️ 미사용 객체의 제거 + 아직 사용중인 객체를 Survivor1, Survivor2로 이동
    (객체의 크기가 Survivor 영역보다 클 경우, 바로 Old Generation으로 이동)
  3. 운영 특성상, Survivor1과 Survivor2 영역은 한 곳에만 객체가 존재하도록 운영.
    (객체가 존재하는 Survivor이 가득 차면, 다른 Survivor로 보내고, 기존의 Survivor을 비우는 작업을 진행)
  4. 위 1~3 과정을 반복하면서 Survivor에서 계속 살아남는 객체는 Score가 누적되며, 일정 기준치를 넘으면 Old Generation으로 이동.
  5. Old Generation에서 살아남았던 객체들이 일정 수준 쌓이면, 미사용으로 판단된 객체들을 제거해주는 Full GC(Serial GC / Parallel GC / Parallel Compacting GC 등)가 발생

 

참고 링크

https://www.youtube.com/watch?v=jXF4qbZQnBc  

https://www.youtube.com/watch?v=vZRmCbl871I

 

'CS' 카테고리의 다른 글

[Java] Runnable, Thread 예시  (0) 2024.04.07
[컴퓨터구조] 캐시 메모리  (0) 2024.03.10
[컴퓨터구조] 총정리  (0) 2024.03.10
[JAVA] 객체 지향 설계의 5원칙 - SOLID 정리  (0) 2024.03.10
운영체제 - 프로세스와 스레드  (0) 2024.03.10