GC (Garbage Collection) 이란?
자바 가비지 컬렉션(Garbage Collection, GC)은
자바 가상 머신(Java Virtual Machine, JVM)에서 동작하는 메모리 관리 기법 중 하나다.
이는 프로그램에서 동적으로 할당된 메모리 중에서 더 이상 사용되지 않는 객체(가비지)를
자동으로 탐지하고 제거하여 메모리 누수를 방지하고 메모리 사용량을 최적화하는 역할을 한다.
GC (Garbage Collection)가 필요한 이유?
메모리 누수 방지
프로그램에서 객체를 생성하면, 해당 객체는 동적으로 할당된 메모리 영역에 저장된다.
이후 해당 객체를 사용하지 않더라도,
개발자가 명시적으로 메모리를 해제하지 않는다면 해당 객체가 차지하는 메모리는 계속 유지된다.
이러한 상황이 지속되면, 시스템에 사용 가능한 메모리가 감소하게 되고,
결국 시스템이 느려지거나 중단될 수 있다.
GC는 이러한 메모리 누수를 방지하여 시스템의 안정성과 성능을 유지하는 역할을 수행한다.
자원 효율성 개선
GC는 더 이상 사용되지 않는 객체를 제거하여 메모리 사용량을 최적화한다.
이를 통해 시스템이 할당된 메모리를 더 효율적으로 사용할 수 있게 된다.
또한, GC는 시스템이 자원을 최적으로 활용할 수 있도록
객체 생성 및 메모리 관리를 자동으로 처리함으로써 개발자의 부담을 줄여준다.
편의성
GC를 사용하면 개발자는 명시적으로 메모리 관리를 수행할 필요가 없다.
따라서 개발자는 보다 집중적으로 코드의 로직과 기능 개발에 집중할 수 있으며,
시스템의 안정성과 성능을 유지하는 데 보다 많은 시간과 노력을 할애할 수 있게 된다.
보안성
GC는 자동으로 객체를 제거하기 때문에,
개발자가 객체를 제거하는 작업을 수행하면서 발생할 수 있는 실수를 방지해 준다.
이를 통해 시스템의 보안성과 안정성을 높일 수 있다.
GC를 구현하는 대표적인 알고리즘
Reference Counting
Reference Counting은 메모리 관리 기법 중 하나로,
객체가 참조되는 횟수를 계산하여 객체가 더 이상 참조되지 않을 때 메모리에서 제거하는 기법이다.
이 방법은 메모리 누수를 방지하고 메모리 사용을 최적화하는 데 유용하다.
Rootspace를 중심으로
(Rootspace는 스택 변수, 전역 변수 등 heap 영역 참조를 담은 변수라고 생각하면 된다.)
Heap 영역에 선언된 객체들이 각각 reference count라는 숫자값을 가지고 있다.
여기서 reference count는 몇 가지 방법으로
해당 객체에 접근할 수 있는지를 의미한다.
해당 객체에 접근할 수 있는 방법이 하나도 없다면,
즉 reference count = 0 이 된다면, GC의 대상이 되는 것이다.
하지만, Reference Counting 은 순환 참조(circular reference)의 문제를 가지고 있다.
위와 같이 Root Space에서 참조가 끊긴 객체가 있다고 가정해 보자.
위에서 주황색으로 표시된 객체들은
Reference count 가 1로 유지되므로, GC의 대상에서 제외된다.
결국 사용하지 않는 메모리 영역이 해제되지 못하고
메모리 누수가 발생하는 것이다.
Mark And Sweep
Mark and Sweep은 가비지 컬렉션 알고리즘 중 하나로,
동적으로 할당된 메모리에서 더 이상 사용되지 않는 객체를 식별하고 해제하는 데 사용된다.
해당 알고리즘은 Reference Counting 알고리즘의 순환참조 문제를 해결할 수 있다.
Mark and Sweep 알고리즘은 mark&sweep 두 단계로 이루어진다.
첫 번째 단계는 mark(표시) 단계이다.
이 단계에서는 모든 할당된 객체를 순회하며, 사용 중인 객체들을 표시하는 데 사용되는 플래그 또는 비트를 설정한다.
Root Space에서부터 해당 객체에 접근할 수 있다면 Reachabled Object
Root Space 에서 부터 해당 객체에 접근할 수 없다면 Unreachable Object로 판단한다.
Reachable Object는 아래 그림의 초록색 부분으로
Unreachable Object 아래 그림의 하얀 부분으로 표시했다.
mark 단계가 완료되면, 두 번째 단계인 sweep(정리) 단계가 실행된다.
이 단계에서는 mark 되지 않은 모든 객체(Unreachable Object)를 삭제하고,
mark를 지운 후에는 메모리에서 할당을 해제한다.
이를 위해 모든 객체를 순회하며, mark 되어 있지 않은 객체는 삭제된다.
하지만, 해당 알고리즘도 완벽하지는 않다.
일단, 의도적으로 GC를 실행시켜야 한다.
즉, 어느 순간에는 실행 중인 애플리케이션이
GC에게 리소스들을 내줘야 한다는 의미가 된다.
따라서, 애플리케이션의 사용성을 유지하면서 효율적으로 GC를 실행하는 것이
어려운 작업이 될 수 있다.
GC의 Heap 영역
Mark and Sweep 방식에서는 의도적으로 GC를 실행시켜야 했다.
그럼 어떠한 타이밍에 GC를 실행시키는지 알아보자.
JVM의 HEAP 영역은 아래와 같이
크게 두영역 Young Genearation과 Old Generation으로 나뉜다.
Young Genearation 발생하는 GC를 Minor GC라고 부르고
Old Generation에서 발생하는 GC를 Major GC라고 부른다.
Young Genearation는 다시 세영역
Eden, Survival0, Survival1으로 나뉜다.
Eden 은 새롭게 생성된 객체들이 할당되는 영역이고,
Survival0/1은 minor GC로부터 살아남은 객체들이 존재하는 영역이다.
Survival 영역에는 하나의 규칙이 있는데,
Survival0 또는 Survival1 중에 하나는 항상 비워진 상태여야 한다는 것이다.
위와 같이 새로운 객체는 Eden 영역에 할당될 것이고,
숫자는 age-bit를 뜻한다.
시간이 경과하면 Eden 영역이 꽉 차는 순간이 올 것이다.
이때 minor GC 가 발생한다.
mark and sweep 방식으로 root로부터 Reachable이라고 판단된 객체들은
S0으로 이동하게 되고, 객체들의 age-bit 가 1씩 증가하게 된다.
시간이 좀 더 흘러 다시 eden 영역이 꽉 차서
다시 minor GC 가 발생되었다고 해보자.
이번에는 Reachable이라고 판단된 객체들이 S1으로 이동한다.
그리고 다시 eden 영역이 다시 꽉 차게 되면,
minor gc 가 발생하게 되고, Reachable 한 객체는 다시 S0 영역으로 이동할 것이다.
이와 같은 현상이 계속 일어난 후에는
특정 객체의 age-bit 가 일정 수준의 값이 넘어가는 시기가 찾아올 것이다.
이럴 경우에 JVM 은 해당 객체가 오래도록 참조될 객체로 판단하여,
위의 그림과 같이 특정 일정 수준의 값이 넘은 객체들을 Old generation으로 이동시킨다.
해당 현상을 Promotion이라 부른다.
시간이 지나 아래와 같이 Old generation 도 모두 다 채워지는 상황이 발생할 것이다.
이때 Major GC 가 발생하면서 필요 없는 메모리를 비워준다.
해당 Major GC의 작업은 Minor GC 의 작업보다 더 오래 걸린다.
'JAVA' 카테고리의 다른 글
[JAVA] JAVA 메모리 영역과 JVM 구조 - Stack, Heap, Method (0) | 2023.03.07 |
---|---|
[JAVA] equals와 ==의 차이점 (0) | 2022.06.02 |
[JAVA] StringTokenizer (0) | 2022.03.23 |