본문 바로가기

Java

[Java] Garbage Collector 대한 공부

Garbage Collector

프로그램이 동적으로 할당했던 메모리 영역(Heap) 중 사용하지 않는 영역을 탐지하여 해제하는 기능이다.

public class Main{
    public static void main(String[] args){
    	User user=new User("홍길동","20")
        user=new User("김철수","25")
    }
}

위 코드를 보면 user는 처음에 User 객체가 생성되고 '홍길동'을 참조하고 있다. 하지만 다음줄이 실행되면서 '김철수'를 참조한다. 이때 참조하지 않는 '홍길동'이 GC의 대상이 된다. 

 

GC에 과정으로 Mark and Sweep있다. 처음에는 GC가 Stack의 모든 변수를 스캔하면서 객체가 참조하고 있는 변수를 Marking 한다. 이때 reacheable object가 참조하는 객체도 Marking 한다. Marking 되지 않는 객체를 Sweep 한다.

reachable object

heap 영역 내부의 객체들 중 Method Area, Stack, Native Stack에서 참조하고 있는 객체

Mark

사용되는 메모리는 와 사용되지 않는 메모리를 식별하는 과정이다.

Sweep

Mark 되지 않은 객체들을 해제시키는 과정이다. 

 

heap 

동적으로 할당한 메모리 영역이다. 객체 데이터를 heap영역에 할당한다. 

객체를 가리키는 참조 변수를 Stack에 할당한다.

 

 

heap은 객체가 생성하고 Stack에 참조되다가 Stack에 참조가 되지 않으면 Mark 되지 않고 Sweep이 일어난다. 즉 GC가 일어난다. 여기서 GC를 할 때 Heap 영역에서 일어나냐에 따라서 달라진다.

 

Young 

Eden

새로 생성한 대부분의 객체가 위치한다.

Eden영역이 차게 되면 Minor GC이 실행된다.

Mark and Sweep이 일어난다.

 

 

Survivor0 / Survivor1

 

Eden영역이 차게 돼서 Minor GC가 일어나면 살아남은 객체가 배치된다. 이때 배치되는 곳이 Survivor인데 공간을 구분하기 위해서 0과 1을 나 누거지 우선순위가 있는 것이 아니다. 두 영역 중 하나는 반드시 비어 있어야 한다. GC가 일어나면 객체 안에 Age가 증가하게 된다. 

위 그림에서 Survivor0 영역이 차게 되어 Minor GC가 일어나면 살아남은 객체가 비워져 있는 Survivor1 영역에 배치된다. 

 

 

Old

Young 영역에서 특정 횟수이상 GC가 되면 Old에 배치된다.

Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 GC가 적게 발생한다.

10은 특정 횟수 이상이 돼서 Old영역으로 넘어간다는 것을 표시하기 위해서 색을 다르게 표시했습니다. 

Minor GC

위에 그림들에서 일어난 GC가 Minor GC이다. young영역에서 실시하는 GC를 이야기한다. 

Major GC

Old 영역에 객체가 차면 일어난다. GC 실행 중에 GC를 제외한 모든 스레드를 중지한다.

 

Old영역이 차게 되고 Major GC가 일어난다. 이때 Stop-The-World가 일어난다. 

Stop-The-World

GC가 나누어진 이유이다. GC를 통해서 여유 메모리를 확보할 수 있지만 실제로 GC를 자주 실행하면 성능이 저하된다. 그래서 성능을 늘리기 위해서 구역을 나누고 오래 사용한 객체를 GC 할 때만 GC스레드를 제외한 모든 스레드의 작동을 일시적으로 정지한다. 이를 stop-the-world라고 한다.

 

 

 

GC 방식들

Serial GC (-XX:+UseSerialGC)

Young 영역에서는 Minor GC를 사용한다. Old 영역의 GC는 mark-sweep-compact이라는 알고리즘을 사용한다.

 

이 알고리즘은

1. Old 영역에 살아 있는 객체를 식별(Mark)하는 것이다. 

2. 힙(heap)의 앞부분부터 확인하여 살아 있는 것만 남긴다(Sweep).  

3. 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눈다(Compaction).

 

 

Serial GC는 데스크톱의 CPU 코어가 하나만 있을 때 사용하기 위해서 만든 방식이다. 그래서 애플리케이션의 성능이 많이 떨어진다.

Parallel GC (-XX:+UseParallelGC)

Serial GC와 기본적인 알고리즘은 같다. 하지만 Parallel GC는 GC를 처리하는 스레드가 여러 개다.

Minor GC에서만 동작한다.

위 그림처럼 스레드를 여러 개를 사용하기에 빠르게 처리할 수 있다. 자세히 보면 윗그림보다 GC에서 화살표의 길이가 짧다.

Parallel Old GC(-XX:+UseParallelOldGC)

Parallel GC를 개선한 버전으로 Minor GC와 Major GC에서 동작한다. Old영역에 대해서는 Mark-Summary-Compact 알고리즘을 사용한다. Summary 단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아있는 객체를 식별한다. 이점에서 Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거친다.

CMS GC (-XX:+UseConcMarkSweepGC)

 

초기 Inital Mark 단계에서는 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 끝낸다. 따라서 멈추는 시간이 매우 짧다.

 

Concurren Mark 단계에서는 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다. 이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다. 

 

그다음 Remark 단계에서는 Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다. 마지막으로 Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행한다. 이 작업도 다른 스레드가 실행되고 있는 상황에서 진행된다.

 

이러한 단계로 진행되는 GC 방식이기 때문에 stop-the-world 시간이 매우 짧다. 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용한다. 

 

CMS GC은 다른 방식들보다 메모리와 CPU를 많이 사용한다. 그리고 Compaction 단계를 기본적으로 제공하지 않는다.

G1 GC

JDK 9부터 G1 GC가 default로 제공된다. 

region으로 구분된 영역으로  Eden, Suvivor, Old, Humonogous, Available/Unused 영역이 있다.

Humonogous은 Region 크기의 50%를 초과하는 객체체를 저장한다.

Available/Unused은 사용되지 않은 Region을 의미한다.

 

위 그림에서 보듯이, G1 GC는 Heap 영역을 region 별로 구분해서 Heap 영역을 바둑판처럼 만든다. 해당 영역이 꽉 차면 다른 영역에 객체를 할당하고 GC를 실행한다. Heap메모리가 클수록 잘 동작한다.

 

Mixed GC

Young 영역과 Old 영역의 Garbage를 수집한다. 한 번에 수집하기는 너무 크기에 나누어서 수행한다. Minor GC와 유사하지만, 추가로 Old 영역 Garbage를 수집한다.

 

Major GC

Old 영역에서 수행된다. 전체 Heap을 스캔하고 참조되지 않는 객체를 제거한다. 

 

출처

https://dzone.com/articles/java-memory-architecture-model-garbage-collection

https://techblog.woowahan.com/2628/

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

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

https://d2.naver.com/helloworld/1329

https://mangkyu.tistory.com/118

https://velog.io/@ddangle/Java-GC-Garbage-Collector%EC%97%90-%EB%8C%80%ED%95%B4#%ED%9E%99-%EC%98%81%EC%97%AD%EC%9D%98-%EC%84%B8%EB%B6%84%ED%99%94