-
[JAVA] 자바 가비지 컬렉션 , Java Garbage Collection #GCJAVA 2021. 7. 6. 23:35
자바 가비지 컬렉션 , Java Garbage Collection
안녕하세요? 장장스입니다.
자바의 GC에 대해 정리해보겠습니다.
GC (Garbage Collection)
자바 어플리케이션은 JVM에 의해 구동이 됩니다. GC(Garbage Collection)는 JVM의 주요 기능으로 할당한 메모리 영역 중 사용하지 않는 영역을 탐지하여 해제하는 작업을 합니다.
자바의 GC(Garbage Collection)는 아주 단순한 규칙을 갖고 있습니다.
Heap 영역의 오브젝트 중 stack 에서 도달 불가능한 (Unreachable) 오브젝트들은 가비지 컬렉션의 대상이 된다.
자바의 메모리 영역, heap
public class Main { public static void main(String[] args) { int number1 = 10; int number2 = 30; String name = "zangzangs"; String gender = "man"; System.out.println(name); System.out.println(gender); } }
위와 같은 코드의 Main이 동작하면 어떻게 될까요?
순차적으로 number1, number2 가 stack 쌓이게 됩니다.
다음으로 name, gender가 stack에 쌓이고,
Object인 String이 heap 영역에 생성되며 name, gender는 heap 영역의 참조값을 갖게 됩니다.
Main이 종료되면 stack에 저장된 데이터는 삭제가 됩니다. 그러나 heap 메모리 영역에 생성된 객체들은 아래 그림과 같이 여전히 남게 됩니다.
더 이상 아무도 참조하지 않게 된 heap 영역의 String 객체는 사용되지 않고 남아 있는데 이를 Unreachable Object라 합니다. 반대로 계속해서 참조되고 있는 객체를 Reachable Object 라고 합니다.
GC Roots(Stack 영역의 데이터, 메서드 영역의 static 데이터, JNI에 의해 생성된 객체)로 부터 유효한 참조가 존재하는지를 확인(mark)하고 유효하지 않은 객체가 있다면 삭제(sweep)하는 과정을 mark and sweep 이라고 합니다.
Mark and Sweep, 그리고 compaction
Mark and Sweep은 GC가 동작하는 가장 기본적인 원리입니다. 동작 과정은 다음과 같습니다.
Step 1: Marking
가장 먼저 가비지 컬렉터가 메모리 조각들로부터 사용을 하는지 안하는지를 확인하는 작업을 합니다. 어떤 객체가 Reachable 혹은 Unreachable 한지 마킹(Marking)하는 과정을 거치게 됩니다.
Step 2: Sweep
마킹이 끝나면 더 이상 참조되지 않는 Unreachable object들을 제거하게 됩니다. Sweep이라고 합니다.
Step 2a: Compact
Sweep이 발생하게 되면 메모리 주소에 공간이 텅텅 비게 됩니다. 분산된 객체들을 Heap의 시작 주소로 모으는 작업을 하게 됩니다. GC 알고리즘에 따라 Compact가 이루어지지 않기도 합니다.
Heap 구조
- Young Generation
- 새로운 object들이 할당되는 영역
- Eden, Survivor 영역으로 나뉜다.
- Old Generation
- Young Generation에서 오랫동안 살아남은 object들이 존재하는 영역
- Permanent generation
- JVM이 어플리케이션에서 사용할 클래스와 메서드를 구성하는데 필요한 메타 데이터가 존재하는 영역
GC의 작업과정
Heap 내에서 GC는 크게 두가지로 나뉘어서 발생하게 됩니다.
Young Generation에서 발생하는 Minor GC와 Old Generation에서 발생하는 Major GC가 있습니다
Minor GC는 어떻게 동작할까요?
최초에 객체가 생성이 될 때 Young Generation 내의 eden 영역에 생성이 됩니다. 계속해서 객체가 생성이 되어 eden 영역이 가득(Full) 차게 되면 Minor GC가 일어납니다.
먼저, 객체들을 검사하여 참조되고 있는지 아닌지 체크하는 Mark하는 과정이 일어납니다. 이후 더 이상 사용되지 않는 Unreachable Objects를 제거하는 Sweep이 일어나게 됩니다.
Mark and Sweep 이후에 살아남은 객체들은 Survivor 영역으로 옮겨지게 되며 Age bit 값이 증가하게 됩니다.
참고로 Survivor 영역은 0과 1로 나뉘게 되는데 순서는 중요하지 않고 Minor GC 마다 번갈아가며 사용됩니다.
이처럼 Minor GC 과정이 반복되다 보면 객체가 가진 Age bit 점점 높아지게 됩니다. Age bit이 특정 임계점에 도달하면 Old Generation영역으로 이동하게(실제로는 주소 공간에 대한 포인터가 변경 되는 것) 됩니다. 여기서는 9를 임계점이라고 가정해보겠습니다.
Major GC는 언제 일어 발생할까요?
Age 값이 임계점에 도달한 객체들이 Old Generation에 가득 차게 되면 Major GC가 일어나게 됩니다. Major GC 또한 사용하지 않는 객체를 삭제하는 Mark and Sweep 작업이 이루어지게 됩니다. 단, Major GC는 10배 이상의 시간을 사용하기에 Major GC가 자주 발생하게 되면 성능에 나쁜 영향을 주게 됩니다.
따라서 Heap 메모리 영역에서 Young Generation 과 Old Generation 을 적절하게 분배하는 것이 중요합니다.
stop-the-world란?
GC을 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것을 말한다. stop-the-world가 발생하면 GC를 실행하는 쓰레드를 제외한 나머지 쓰레드는 모두 작업을 멈추고 GC가 작업을 완료한 이후에야 중단했던 작업을 다시 시작한다. 어떤 GC 알고리즘을 사용하더라도 stop-the-world는 발생한다.
GC의 성능을 높이는 주요 방법 중 하나가 바로 stop-the-world 시간을 줄이는 것이다.
Permanent Generation 영역
자바8 이전에는 클래스의 메타 데이터를 저장하는 공간을 Heap 메모리에 사용했었다.
자바8이 사용되면서 부터 PermGen(Permanent Generation) 영역은 더 이상 사용하지 않고, MetaSpace 영역을 사용한다. 때문에 자바8부터는 PermGen 관련 옵션은 모두 무시한다.
MetaSpace 영역은 클래스의 메타데이터를 담는 곳으로 Heap영역이 아니라 Native 메모리 영역이다.
기본 값으로 제한된 크기를 가지고 있지 않으며, 필요한 만큼 계속 늘어날 수 있다. 무한정으로 늘어날 수도 있기 때문에 대규모 프로젝트에서 native 메모리를 초과해서 필요로 하는 메모리 누수가 발생하기도 한다.
참고로 초기 설정을 통해 MetaSpace 를 설정 할 수 있다.
- XX:metaspaceSize : 초기 사이즈
- XXMaxMetaSpaceSize : 최대 사이즈
여러가지 GC 알고리즘
Serial GC
- GC를 처리하는 쓰레드가 1개(싱글 쓰레드)
- 다른 GC에 비해 Stop-the-world 시간이 길다
- Mark-Compact(Sweep포함) 알고리즘 사용
- 현대에서 사용하지 않음
Parallel GC
- JAVA 8 의 디폴트 GC
- Young Generation의 GC를 멀티 쓰레드로 수행함
- Serial GC에 비해 stop-the-world 시간 감소
Parallel Old GC(Parallel Compacting GC)
- Parallel GC 를 개선
- Old Generation 에서도 멀티 쓰레드 방식의 GC 수행
- Mark-Summary-Compact 알고리즘 사용
Concurrent Mark & Sweep GC(이하 CMS)
- stop-the-world 시간을 줄이기 위해 고안됨
- compact 과정이 없음 -> 메모리 단편화 문제
G1(Garbage First) GC
- CMS GC를 개선
- JAVA 9+ 의 디폴트 GC
- Heap을 일정한 크기의 Region으로 나눔
- 전체 Heap이 아닌 Region 단위로 탐색
- compact 진행
References
- https://d2.naver.com/helloworld/1329
- https://www.youtube.com/watch?v=Fe3TVCEJhzo
- https://www.youtube.com/watch?v=vZRmCbl871I
- https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
잘못된 코드나 내용이 있다면 댓글을 남겨주세요. 즉시 수정하도록 하겠습니다! :)
'JAVA' 카테고리의 다른 글
자바 람다 표현식 설명부터 사용법까지 완벽 정리 #JAVA #람다 (0) 2023.01.03 [객체 지향 설계] SOLID 원칙 (0) 2021.09.01 [JAVA] LinkedList를 접근 제어하는 ListIterator (0) 2021.01.24 [JAVA] DTO(VO) 리스트(list)를 특정 변수에 대해 정렬하기 (0) 2020.12.14 [JAVA] Number 클래스와 Value 메서드 (0) 2020.07.13 - Young Generation