Garbage Collector(GC)
- 메모리 관리 방법 중 하나로 시스템에서 더이상 사용하지 않는 동적 할당된 메모리 블럭을 찾아 자동으로 다시 사용 가능한 자원으로 회수하는 것으로 시스템에서 가비지 컬렉션을 수행하는 부분을 가비지 컬렉터라 부릅니다.
- C언어의 경우, 프로그래머가 메모리를 할당한 뒤 수동으로 해제까지 직접 해줘야 합니다. 그래서 메모리를 할당해놓고 필요없어진 뒤에도 해제를 안하는 일 등이 생겨서 메모리 누수가 생기거나 거꾸로 해제했던 메모리를 다시 사용하는 실수가 생기곤 합니다.
- 이러한 문제들을 해결하기 위해 생겨난 것이 가비지 컬렉션이며, 가비지 컬렉션 기능을 채택한 언어의 경우에는 가비지 컬렉터가 자동으로 프로그램이 실행되며 생기는 쓸모 없는 메모리들을 알아서 수집하고 관리해줍니다.
- 즉, 프로그래머는 Heap을 사용할 수 있는 만큼 자유롭게 사용하고, 더이상 사용되지 않는 객체들은 가비지 컬렉션을 담당하는 프로세스가 자동으로 메모리에서 제거하도록 하는 것이 가비지 컬렉션의 기본 개념입니다.
- JVM의 메모리는 크게
Class 영역, Stack, Heap, Native Method Stack
의 4개 영역으로 나뉩니다. 이중 가비지 컬렉터가 인식하고 할당하는 메모리 영역은Heap
영역입니다.
Garbage 란?
Garbage란 '정리되지 않은 메모리', '유효하지 않은 메모리 주소'를 말합니다.
아래 예시를 통해 자세히 알아보겠습니다.
String str = "Garbage";
str += " Collector";
위 코드에서 String str = "Garbage"
구문이 실행된 후 Stack과 Heap의 상태는 아래와 같습니다.
그리고 다음 구문인 str += " Collector";
구문에서 문자열 더하기 연산이 수행될 때, 기존에 있던 "Garbage"
String 값에 " Collector"
를 덧붙이는 것이 아니라, 문자열에 대한 더하기 연산이 수행된 결과가 새롭게 Heap 영역에 할당됩니다. 이를 표현하면 아래와 같습니다.
위처럼 Stack에는 새로운 변수가 할당되지 않습니다. 그저 Heap 영역에 새롭게 "Garbage Collector"
가 생성되고, 기존에 "Garbage"
를 참조하던 Stack 영역의 str
변수는 새롭게 생성된 "Garbage Collector"
를 참조하게 됩니다.
이 과정에서 Heap 영역의 "Garbage"
를 참조하고 있는 Stack의 변수는 아무것도 없으므로 Unreachable
객체가 되고, 이를 Garbage
라고 합니다.
즉, 가비지 컬렉터는 메모리가 부족할 때, 이러한 Garbage
를 정리해주는 프로그램을 말합니다.
프로그램을 실행하다보면 Garbage
가 계속해서 발생하게 되는데, 이들은 유효한 메모리가 아닙니다.
이렇게 생겨난 Garbage들은 사용되지도 않으면서 메모리를 차지하고 있게 됩니다.
따라서, JVM의 가비지 컬렉터는 Garbage
를 다른 용도로 사용할 수 있게 '메모리 해제'시키는 프로그램입니다.
Garbage Collector의 원리
GC 작업을 하는 가비지 컬렉터는 다음과 같은 일을 합니다.
- 메모리 할당
- 사용 중인 메모리 인식
- 사용하지 않는 메모리 인식
즉, 메모리가 부족할 때 가비지를 정리해주는 프로그램을 가비지 컬렉터라고 부릅니다. 가비지 컬렉터에 대해 알기 전에 우선 메모리에 대한 이해가 필요한데 프로그램을 실행할 때 메모리를 관리하는 OS에 프로그램 실행에 필요한 메모리를 요청하게 됩니다. 이때 메모리를 어디에 저장할지 그 주소를 할당하는데 이 주소를 offset 주소(상대주소)라고 부릅니다.
이 할당된 메모리들은 프로그램이 돌아가면 필연적으로 가비지가 발생하게 됩니다. 기존에 가리키고 있던 메모리가 새롭게 선언되거나 형변환이 되면서 다른 곳을 가리키게 되고 주소를 잃어버리게 되어 다시 찾을 수 없게 되면서 정리되지 않은 메모리가 생겨버리게 되기 때문입니다.
그래서 가비지 컬렉터는 가비지를 다른 용도로 사용할 수 있도록 메모리를 해제시킵니다. 이것이 가비지 컬렉터의 목적입니다.
Java 기준으로 JVM은 메모리를 부여받고 프로그램을 실행하다가 메모리가 부족해지는 순간이 오면 추가적으로 메모리를 더 요청합니다. 요청하는 바로 이때 가비지 컬렉터가 실행됩니다.
또한, 서버 프로그램의 경우 24시간 내내 돌아가는데, 이 때에는 JVM이 한가할 때(Idle time) 가비지 컬렉터가 실행됩니다.
Stop-the-world
Stop-the-world는 GC 실행을 위해 JVM이 애플리케이션 실행을 멈추는 것입니다. 이 Stop-the-world가 발생하면 GC를 실행하는 스레드를 제외한 나머지 스레드는 모두 작업을 멈춥니다. 그래서 대부분의 경우에서 말하는 GC 튜닝이란 이 Stop-the-world의 시간을 줄이는 것을 의미합니다.
GC의 과정을 Mark and Sweep
이라고 부릅니다. 가비지 컬렉터가 닿을 수 있는 모든 변수나 객체들을 스캔하면서 어떤 객체를 가리키고 있는지 찾는 과정을 Mark
라 하고, 이 과정에서 Stop-the-world가 발생합니다. 이후 Mark
되어있지 않은 객체들을 Heap 영역에서 제거하는 과정이 Sweep
입니다.
Gabage Collector의 동작
GC의 공간
- Young 영역
- 새롭게 생성된 객체는 Young 영역에 생성됩니다.
- 대부분의 객체가 금방 접근 불가능한 상태가 되기 때문에 많은 객체가 이 영역에 생성되었다가 사라집니다.
- Minor GC가 발생하는 영역
- Old 영역
- Young 영역에서 참조할 수 있는 상태를 유지해 살아남은 객체가 여기로 복사됩니다.
- 대부분 Young 영역보다 크게 할당하며 크기가 큰 만큼 Young 영역보다는 GC가 적게 발생합니다.
- Major GC가 발생하는 영역
- Permanent 영역 (Method Area)
- 클래스와 메소드 정보와 같이 Java 언어 레벨에서는 거의 사용되지 않는 영역입니다.
- 객체의 Intern(억류)가 된 문자열 정보를 저장하는 곳
- GC가 발생하기도 하는데 Major GC의 횟수에 포함됨
Young 영역에서의 GC
- Young 영역은 Eden 영역, Survivor 영역 2개, 총 3개의 영역으로 나눠짐
- Minor GC가 일어나는 과정
- 새로운 객체가 생성되면 Eden 영역에 생성되며, GC 후 살아남은 객체는 Survivor 영역 중 하나로 이동합니다.
- Survivor 영역이 가득차게 되면 다른 Survivor로 이동하고 나머지 하나의 Survivor 영역은 빈 상태로 변경합니다.
- 이 과정을 반복한 후, 살아남은 객체는 Old 영역으로 이동하게 됩니다.
Old 영역에서의 GC
- Old 영역은 기본적으로 메모리가 가득 차면 GC를 수행합니다.
- Old 영역에서 일어나는 GC는 아래의 5가지 방식으로 수행됩니다.
- Serial GC
- Mark-Sweep-Compact 알고리즘을 이용합니다.
- Old 영역에 살아있는 객체를 Mark 하고 Heap의 앞 부분부터 살아있는 객체를 Sweep 한 뒤, Heap의 앞 부분부터 객체를 쌓습니다(Compact).
- 메모리와 CPU 코어 수가 적을 때 적합한 방식입니다.
- Parallel GC
- Serial GC와 기본적인 알고리즘은 같지만, GC를 처리하는 스레드의 개수가 여러 개입니다.
- 메모리와 CPU 코어 수가 많을 때 적합할 방식입니다.
- Parallel Old GC
- Parallel GC와 같지만 Old 영역의 GC 알고리즘만 다릅니다.
- Mark-Summary-Compact 알고리즘을 이용하며, Mark-Sweep-Compact 알고리즘에 비해 조금 더 복잡합니다. (Summary에서 GC를 수행한 영역에 대해서 별도로 살아있는 객체를 식별합니다.)
- CMS GC
- Class Loader에서 가장 가까운 객체 중 살아있는 것만 찾습니다. 후에 참조가 끊기거나 추가된 객체를 확인하고 마지막에는 가비지를 정리하는 작업을 합니다.
- Stop-the-world의 시간이 짧고 모든 애플리케이션 응답 속도가 매우 중요할 때 사용합니다.
- CMS GC는 다른 방식보다 메모리와 CPU를 많이 사용합니다.
- Compact 단계가 기본적으로 제공되지 않습니다.
- G1 GC
- 바둑판의 각 영역에 객체를 할당하고 GC를 실행합니다.
- 해당 영역이 가득 차면 다른 영역에서 객체를 할당하고 GC를 실행하는 방식입니다.
- Young 영역과 Old 영역의 구분이 없이 매우 빠르게 객체를 할당하고 GC합니다.
- Serial GC
Gabage Collector의 한계
- 어떤 방식의 가비지 수집을 사용하든지 실행 시간에 작업을 하는 이상 성능 저하를 피할 수는 없습니다.
- 가비지 수집기가 존재하더라도 더이상 접근이 불가능한 객체만 회수하기 때문에 메모리 누수는 발생할 수 있습니다.
'Computer Science > Java' 카테고리의 다른 글
JAVA vs C++ (0) | 2021.03.27 |
---|---|
추상 클래스와 인터페이스 (0) | 2020.10.23 |
Java 메모리 - (2) JVM (0) | 2020.10.19 |
Java 메모리 - (1) Java 메모리 구조 (0) | 2020.10.18 |
객체지향 프로그래밍(OOP) (0) | 2020.10.17 |