1. 개요
이 글은 GC에 대한 이해를 돕기 위한 토막글입니다.
필자가 각종 책과 공식 문서를 통해서 정리한 글이지만 오류가 존재할 수 있습니다.
때문에 오류를 발견할 시 댓글을 통해서 알려주시면 감사하겠습니다.
2. 시작하기 앞서 필요한 지식
GC에 대해서 알기 전 JVM에 대해서 가볍게 짚고 넘어가겠습니다.
2.1. JVM이란 무엇일까?
Java를 통해서 개발하는 사람들이라면 JVM이 무엇인지 까지는 제대로 알지 못해도 적어도 들어본적은 있을 것이다.
JVM은 Java Virtual Machine의 약자며 자바를 구동하기 위한 가상 머신이다.
하지만 JVM은 Java Bytecode의 동작 방식을 정해놓은 스펙표의 불과하고 실질적인 구현은 벤더마다 다를 수 있다.
때문에 우리가 인터넷에서 찾아보는 각종 자료는 파편화 되고 여러 지식으로 갈릴 수 밖에 없다.
이 글에서는 Oracle과 Hotspot JVM을 기준으로 작성한다.
3. GC란 무엇인가?
GC는 Garbage Collection의 약자로 한국어로 쓰레기 수집가정도로 해석할 수 있을것 같다.
컴퓨터 세상에서 쓰레기란 사용하지 못하는 메모리를 의미한다.
본래 GC가 나오기 전에는 우리는 모든 메모리의 해제를 직접 관리해야했다. 이를 Unmanaged 언어로 부른다. 대표적으로는 C, C++이 존재한다.
Unmanaged 언어는 메모리의 해제를 직접하는 만큼 더 효율적인 메모리 사용이 가능하지만 개발자의 실수로 해제되지 않는 메모리가 쌓여 Memory leak이 자주 발생했다.
때문에 메모리의 해제를 자동으로 관리하는 GC가 등장하게 되었는데 이를 Managed 언어 즉 우리가 사용하는 Java를 의미한다.
3.1. 얻는게 있으면 잃는것도 있다.
그렇다면 GC는 만능이었을까? 안타깝게도 정말 대다수의 기술은 편리함을 대가로 성능을 가져가기 마련이다.
GC가 작동하는 동안은 모든 Thread들이 멈추고 GC의 작업을 기다리게 된다. 이를 Stop the world라고 부르며 해당 시간동안 Suspend time이 존재하게 된다.
3.2. 걱정거리는 추가로 있다.
실제 Physical Memory의 경우 OS단에서 Paging 혹은 Fragment 기법을 이용해 메모리의 파편화를 방지하고 있다.
그에 비해서 우리가 이용하는 Logical Memory의 경우 OS가 별도의 Memory Managing 기법을 이용해 파편화를 방지해주지 않는다.
3.2.1 파편화가 일어나면 어떤 문제가 있는가?
간단히 말하자면 메모리의 효율적인 이용이 어려워 진다.
상세한 문제는 이 글에서는 다루지 않겠다. 파편화 문제를 확인해보면 상세히 나올것이다.
4. GC가 동작하는 방식
그렇다면 GC는 어떤식으로 동작하고 어떤식으로 쓰레기를 찾을 수 있는걸까?
4.1. Reachable/Unreachable 객체 판별하기
자바의 모든 객체는 Heap 영역에서 생성되고, 기타 영역에서 해당 메모리의 객체를 참조한다.
Heap의 객체를 참조하는 레퍼런스를 Root Set이라 말하고 이를 시작점으로 참조객체를 Mark한다.
이때 Mark되지 않는 객체들은 모두 Unreachable객체로서 GC의 청소대상이 된다.
4.2. GC의 작동 방식
구글에 자바GC를 검색했을 때 얻을 수 있는 결과
2.1. Serial Collector (직렬 콜렉터)
직렬 콜렉터는 가장 간단한 구조의 GC 중 하나로, 우리가 가장 쉽게 접할 수 있는 GC입니다.
직렬 콜렉터는 GC 작업을 수행하는 동안 하나의 쓰레드만 사용합니다. 이는 GC 작업이 시작되면 모든 애플리케이션 쓰레드가 정지하게 됩니다. 따라서 GC 작업이 길어질 경우, 사용 중인 애플리케이션 전체의 성능에 영향을 미칠 수 있습니다.
하지만 직렬 콜렉터는 다른 복잡한 GC 방식에 비해 구현이 간단하고 예측 가능합니다. 따라서 작은 규모의 애플리케이션에 적합합니다.
2.2. Parallel Collector (병렬 콜렉터)
Parallel Collector는 Serial Collector와는 달리 여러 개의 쓰레드를 이용해 GC를 수행하며, Mark와 Sweep 단계에서 병렬 처리를 수행합니다. 이를 통해 GC 작업을 더 빠르게 처리할 수 있습니다.
2.3. CMS Collector (Concurrent Mark Sweep)
CMS Collector는 Parallel Collector의 단점인 Stop-The-World시간을 줄이기 위해 개발된 GC입니다. CMS Collector는 GC 작업을 수행하면서 동시에 작업을 수행합니다. 이를 통해 Stop-The-World시간을 최소화 할 수 있습니다. 하지만, CMS Collector는 처리한 쓰레기를 메모리에서 제거하는 과정에서 메모리 파편화가 발생할 가능성이 있습니다.
2.4. G1 Collector (Garbage First)
G1 Collector는 JDK 7부터 도입된 GC로, 대용량 메모리를 가진 시스템에서 효율적으로 GC 작업을 수행할 수 있도록 설계되었습니다. G1 Collector는 Heap 영역을 여러 개의 region으로 나누어 관리합니다. 각 region은 Eden, Survivor, Old 등의 역할을 수행하며, GC 작업은 이러한 region 단위로 수행됩니다. 이를 통해 Stop-The-World시간을 최소화하면서, 메모리 파편화를 방지할 수 있습니다.
5. 결론
GC는 자바의 편리함을 가져오기 위해 필수적인 요소입니다. 그러므로 GC의 개선을 위해 여러 방법들이 연구되고 있습니다. 이 글에서는 JVM의 GC에 대한 개요와 GC의 작동 방식, 그리고 GC 개선을 위한 여러 가지 방법들을 다뤄보았습니다. 그러나, 이러한 방법들은 각각의 상황에 따라 적용이 쉽지 않을 수 있습니다.
따라서, 개발자들은 자신이 작성한 코드의 GC에 대한 영향을 이해하고, 이를 최적화하는 것이 중요합니다. 특히, 메모리 누수와 같은 문제를 방지하기 위해서는 객체들의 참조를 적절하게 관리하는 것이 필수적입니다. 또한, GC의 성능을 향상시키기 위해 멀티 쓰레드를 이용하는 방법이나, GC가 진행 중인 동안에도 애플리케이션이 동작하는 방법 등도 고려해볼 수 있습니다.
마지막으로, 이 글에서 다루지 않은 GC 관련 기술들도 있으며, 이러한 기술들은 GC의 성능을 향상시키는 데 큰 도움을 줄 수 있습니다. 개발자들은 이러한 기술들을 학습하고, 자신의 코드에 적용하는 것이 좋습니다.
결론적으로, GC에 대한 연구와 이에 대한 최적화는 자바 애플리케이션의 성능을 향상시키는 데 중요한 역할을 합니다. 따라서, 개발자들은 GC에 대한 이해를 깊이 있게 하고, GC를 최적화하는 방법들을 연구하며, 이를 자신의 코드에 적용하는 것이 필수적입니다.