Language/Java

[Java] Garbage Collector란?

ooeunz 2019. 11. 10. 11:49
반응형

GC란?

Machine Code에서 Assembly Language로 넘어오는 시점부터 프로그래머들은 기호를 이용한 프로그래밍을 하기 시작했다. 그리고 그러한 기호식 프로그래밍은 점차 language가 발전함에 따라 지금에 와서는 변수에 데이터 값을 초기화하고 그 값을 활용해 프로그램을 만들어낸다. 이러한 변수에 저장된 값들은 메모리 상의 Heap이라고 불리는 특정 영역에 저장된다.

 

컴퓨터의 메모리가 무한하다면, 잔여량을 걱정하지 않고 마음껏 메모리를 사용해도 되겠지만 안타깝게도 우리의 메모리는 유한하기 때문에, 메모리에 사용하지 않는 값들을 지워주지 않는다면 Memory Leak(메모리 누수)가 발생하게 된다. 그리고 이렇게 세어나간 메모리들이 쌓여서 우리의 메모리를 가득채우게 되면  컴퓨터는 작동을 멈추게 된다. unmanunmanaged language(메모리 관리를 해주지 않는 언어)인 C나 C++과 같은 언어는 malloc과 같은 함수로 직접 메모리에 값을 할당하고 해지해주어야 했다. 하지만 자바를 비롯한 최근에 나온 managed language는 사용하지 않는 메모리를 할당 해지해주는 가비지 컬렉터가 존재한다.

 

가비지 컬렉션 개념은 1958초안된 LISP 랭귀지에서 처음 시작되었다. 하지만 자바가 가비지 컬렉션을 대중화시키는 데에 기여하게 되면서 자바의 대표적인 기능 중 하나로 자리 잡게 되었다. 가비지 컬렉션이 작동하는 방식은 여러 가지가 있는데, 그중 하나인 Mark-and-sweap은 메모리를 쭉 훑으면서 필요한 메모리들만 mark한 다음에 mark 되지 않은 메모리들은 일괄적으로 해지하는 방식이다. 프로그래밍 측면에서 보면 root에서 닿지 않는 변수들을 제거하는 방식이다. 두 번 째는 Reference Counting(참조 카운팅) 방식이 있다. 한 요소가 다른 요소에게 참조되는 횟수를 세어서 그 횟수가 0이 되면 해지하는 방식이다.

 

초기의 자바 가비지 컬렉터는 자바의 동작을 멈추고 가비지 컬렉터를 실행해서 몇 초동안 컴퓨터가 얼어있거나 하는 일들이 발생했다. 최근에는 많은 부분 개선되어서 이러한 경우는 없지만, 여전히 모든 부분의 memory leak을 잡아내지는 못한다. 때문에 프로그래머에게 메모리를 관리하는 능력이 요구되게 된다. 하지만 언어마다, 실행환경마다 그 특성이 다르기 때문에 각각의 언어의 특성과 실행되는 환경을 이해하고, 메모리를 관리하는 능력이 필요하다.

 

 


Generational GC

David ungar님이 1984년 Generation Scavenging: A Non-disruptive High Performance Storage Reclamation Algorithm’이라는 논문을 발표한다. 이 논문의 가성 중에 제시 된것이 바로 Generational GC인데, 쉽게 말해 “대부분의 객체를 일찍 죽는다.”라는 가설이다. (실제 통계로도 생성된 객체의 98%가 곧바로 쓰레기 객체가 된다고 한다.)

 

이러한 경험적 사실을 바탕으로 Generational GC가 디자인된다.

 

Heap 메모리를 Young Generation 영역과 Old Generation영역으로 나눈 뒤에 Young Generation 영역을 주기적으로 청소하고, 오랜 기간 사용되는 객체를 Old Generation으로 보내는 것이 기본 원리이다. 이렇게 함으로서 GC는 매번 힙 전체를 청소할 필요 없이 Young 위주로 청소를 하게 되고, Old 영역에 공간이 부족하게 될 때 Old 영역을 청소하게 된다.

 

이러한 디자인에는 성능 상 두가지 이점이 있는데,

첫 번쨰로는 Young Generation은 Old Generation보다 사이즈가 작고, 힙 공간의 일부분이기 때문에 GC가 전체 영역을 처리하는 것보다 시간이 덜 걸린다. (즉, stop-the-world로 애플리케이션이 중지되는 시간이 짧아진다.)

 

두 번째로는 Young Generation 영역을 한 번에 모두 비우기 때문에, 이 Young Generatoin 부분에 해당하는 연속된 여유 공간이 만들어진다. (Compacting이라고 한다.) 만약 GC가 군데군데 골라서 객체를 제거했다면, 메모리 파편화(memory fragmentation)이 발생하여 연속된 큰 데이터가 들어갈 공간이 부족해질 것이다.

 

이런식으로 계속 Young Generation을 청소하다가 Old Generation 영역이 가득차게 되면 Old Generation 영역을 청소하게 된다.

 

여기서 중요한 용어를 한번 정리하고 가자면, 

GC가 Young Generation 영역을 정리하는 것을 Minor GC라고 하고,

Old Generation 영역을 정리하는 것을 Major GC라고 한다.

 

 

Stop the world

GC가 청소를 시작하면 자바 애플리케이션은 GC를 실행하는 쓰레드를 제외한 모든 쓰레드를 멈추고 GC가 완료된 이후에 다시 쓰레드를 실행상태로 되돌린다. 이러한 Thread의 작업이 멈추는 상태를 Stop the world라고 부른다.

 

 

 

GC의 종류

GC에는 Serial Gabage Collector, Parallel Collector, CMS, G1이라는 네가지 종류의 형태가 존재한다. 네가지 모두 다른 형태의 GC이지만 Minor GC가 발생할때는 종류에 상관없이 모두 stop-the-world가 발생한다. (GC를 동작하는 쓰레드도 싱글 쓰레드로 돌아간다.)

 

Serial GC

가장 간단한 GC로, 주로 32bit JVM에서 돌아가는 싱글 쓰레드 애플리케이션에서 사용된다. 단순 무식한 GC라 Minor GC와 Major GC모두 stop-the-world가 발생한다.

 

 

Parallel GC

64bit JVM이나 멀티코어 유닉스 머신에서 기본 GC로 설정되어 있다. Minor GC와 MajorGC모두 멀티 쓰레드를 사용하기 때문에 Serial GC보다 속도가 빠르다. 하지만 Minor GC와 Major GC 모두 stop-the-world가 발생하는 것은 같다.

 

 

CMS GC

'어떻게하면 GC의 stop-the-world 상태를 줄여볼 수 있을까?라는 고민에서 출발한 GC.

Minor GC때는 여전히 Parallel GC와 같이 멀티 쓰레드로 GC를 동작시킨다. (정확히는 Minor GC를 수행하는 알고리즘 자체는 Parellel과 다르다.) 하지만, Major GC는 stop-the-world가 거의 발생하지 않는다.

 

그 이유는 애플리케이션이 작동하는 중에, 백그라운드에서 쓰레드를 만들어서 Old Generation영역의 쓸모 없는 객체들을 지속적으로 제거해주기 때문이다.

 

이로인해, Major GC시에 stop-the-world가 거의 발생하지 않지만,

백그라운드에서 지속적으로 GC 쓰레드가 돌아야하기 때문에 CPU 리소스를 많이 사용한다는 단점과, 중간 중간 Old Generation에 있는 객체들을 제거하기 때문에 메모리 군데군데가 비워지게 되고 memory fragmentation이 발생한다.

 

따라서, 만약 CPU 리소스가 부족해지거나, 메모리 파편화가 너무 심해져 메모리 공간이 부족해지면 Serial GC를 따라한다. (Old Generation 영역을 싱글 쓰레드로 청소)

 

 

G1 GC

Heap 영역이 매우 큰 머신 (최소 4GB)에서 돌리기 적합한 GC이다. 대신 CMS의 단점을 어느정도 극복하게 된다.

 

Heap 영역을 여러개의 region으로 나누어서 몇몇 region은 Young Generation으로, 나머지 몇몇 영역은 Old Generation 영역으로 사용한다. 물론, Minor GC시에는 Parallel이나 CMS처럼 멀티쓰레드로 정리한다. 그리고 Old Generation 여역도 CMS처럼 백그라운드 쓰레드로 이 영역들을 정히라는데,

 

다만 CMS와 차이점은 중간중간 쓸모 없는 객체들을 삭제하는 것이 아니라, 한 region을 통째로 정리해 버린다. (참조가 없는 객체들은 지워버리고, 사용 중인 객체는 다른 Region으로 복사한다.) 이로 인해 Compacting이 되므로 메모리 파편화 현상이 발생하지 않는다.

반응형