가비지 컬렉터
이전에(2018-08-03-가비지컬렉터(C#))와 관련된 포스팅을 한적이 있다. 이번에는 JAVA, 안드로이드 에서의 가비지 컬렉터는 무엇이 다를까 적어보려 한다. 과연 어떤 차이가 있을까?
사실 의미상의 차이는 없다. 가비지(사용되지 않는, 참조되지 않는 메모리)를 컬렉팅(수집, 회수후 해제)해주는 것이다.
C# 처럼 참조루트로 관리하여, 루트에서부터 참조 되지 않는 객체들은 수집대상이다. [출처 : 금광캐는광부 tistory, https://itmining.tistory.com/24] 참조루트(Root Set of References)에서 부터 탐색되지 않는 노드(객체)들은 finalize()를 메모리에서 제거하기 전에 호출한다. 위 그림에서 Unrechable Objects 들이 그러하다.
JAVA 의 가비지 컬렉터(이하 JAVA GC) 또한 JVM 의 힙영역에서 메모리를 관리한다. [출처 : 12bme tistory, https://12bme.tistory.com/57]
위는 JVM의 힙 영역을 여러 구역으로 나눈것이다. 크게 3부분으로 먼저 나뉜다
- Young : 메모리가 처음 할당되고 일정 주기 살아남았을 경우 머무는 영역
- Eden : 메모리가 가장 먼저 할당될때 지정되는 곳
- Survivor : Eden에서 쌓인 메모리가 일정 주기가 지난후 이동 되는 영역 이곳은 Survivor1, 2로 나뉘며 둘중 한 영역은 반드시 비어있다.
- Old : Young 영역에서 계속 살아남아 Young 영역이 차게 되면 이동하는 영역
- Permanent : 클래스와 메소드 영역, 언어단에서 사용되지 않음
GC의 대상이 되는 경우
- 모든 참조가 해제된 경우.(참조 자체가 null 인 경우)
- 블럭({ … }) 안에서 생성되고 사용이 끝난 경우
- 부모 객체가 null이 되는 경우 해당 객체의 자식은 수집 된다
- weak 참조만 가지고 있는경우
- soft 참조이지만 메모리가 부족한 경우 (Java에서 제공하는 soft reference 클래스로 만든 객체)
사실 말만 다르지 개인적으로 C#에서의 0세대, 1세대, 2세대와 다르지 않은거 같다.
GC 종류
위에서 설명한 영역에서 발생하는 GC는 서로 다른 종류이다.
- Minor GC : Young 영역에서 발생하는 GC. 가장 빈번하게 발생
- Major GC : Old 영역에서 발생하는 GC. Young 영역에서 살아남고, 크기가 큰 객체들이 위치하는 만큼 빈번하지는 않지는 않다. Full GC 라고도 한다.
GC 방식 4종류
1. Serial Collector(시리얼 컬렉터)
싱글스레드 머신에서 사용되는 기본 GC. Young 영역과 Old 영역이 연속적으로(시리얼)하게 진행되며, 이때 어플리케이션의 수행은 중지됨 Young -> Old 영역으로 이동한 객체들은 Mark-sweep-compact 알고리즘을 따라 수집된다.
Mark-sweep-compact 알고리즘을
a. Old 영역에서 살아있는 객체를 식별(Mark) b. 식별한 Old 영역 객체들중 수집될 가비지 판별(sweep) c. 가비지는 지우고, 살아남을 객체들은 분리(compact)
주로 대기시간이 길어도 상관없는 작업에 많이 이용된다고 한다. 참고로 어플리케이션의 수행이 중지되는 시점을 Stop the world 라고 부른다고 한다.
2. Parallel Collector(병렬 컬렉터)
multi-CPU 유닉스 머신이나, 64-bit JVM 머신에 사용되는 기본 컬렉터다. Throughput Collector 라고도 부른다. 시리얼 컬렉터와 달리 Young 영역에서 가비지 컬렉션이 병렬적으로 일어나며 Old 영역에서는 시리얼 컬렉터와 마찬가지로 Mark-sweep-compact 알고리즘을 따른다. [출처 : 12bme tistory, https://12bme.tistory.com/57]
병렬로 처리되는 덕분에 시리얼 콜렉터 보다 어플리케이션의 수행 정지가 적다고 한다.
Old 영역에서 Mark-Summary-Compact 알고리즘을 따를 경우 Parallel Old GC라고 부른다. sweep 과 summary 의 차이는 sweep은 단일 스레드가 객체들을 훓었다면(가비지 판별) summary는 여러 스레드가 Old 영역을 나누어 훓는 것이다. 또한 효율을 위해 앞선 GC에서 Compaction된 영역을 별도로 훑는다.
3. CMS Collector
low-latency collector 라고도 불림. 시리얼/병렬 컬렉터에서 Long pause(수행 중지가 오래 진행) 되는걸 막기위해 디자인 되었다고 한다. Young 영역에서는 병렬컬렉터와 같지만 Old 영역에서 차이가있다. Stop the world가 발생한 이후 다음과 같이 진행된다.
Initial marking : 살아있는 객체 판별 Concurrent Mark : 참조를 따라가면서 표시 Remark : Concurrent mark 단계에서 새로 추가되거나 끊긴 객체를 재판별 Concurrent Sweep : 수집된 가비지를 정리한다 별도의 Compaction(구분)이 없다
위에서 보듯이 별도의 Compaction 이 없고, CPU 사용량이 많아 이 때문에 메모리 단편화가 많을수 있어 오히려 Stop the world의 시간이 더 늘어날수 있다.
4. G1 Collector(Garbege First 컬렉터)
JAVA 7 에서 새롭게 소개된 GC. 4GB가 넘는 큰 힙을 처리하기 위해 디자인 된 컬렉터다. 동일하게 Stop the world를 최소화 하며, 힙을 여러 영역으로 나누어 GC를 진행한다. 이전의 GC들은 Young 영역과 Old 영역을 같는다. 하지만 G1의 경우에는 [출처 : Preamtree의 행복로그, https://preamtree.tistory.com/118]
위처럼 영역이 각각 Eden, Survivor, Old영역의 역할을 동적으로 바꿔가며 GC가 일어난다. Young 영역의 GC와 Old 영역의 GC는 모두 CMS콜렉터 방식과 유사하다.