호딩클라우드

[java] JVM Garbage Collector 본문

java

[java] JVM Garbage Collector

hoding-cloud 2024. 3. 4. 14:19

Garbage Collector란?

GC(가비지 수집기)는 애플리케이션의 동적 메모리 할당 요청을 자동으로 관리합니다.

Garbage Collector가 하는 작업들

  • 운영 체제에 메모리를 할당하고 반납합니다.
  • 애플리케이션이 요청할 때 해당 메모리를 애플리케이션에 전달합니다.
  • 해당 메모리의 어느 부분이 애플리케이션에서 아직 사용되고 있는지 확인합니다.
  • 애플리케이션에서 재사용할 수 있도록 사용되지 않은 메모리를 회수합니다.

Java HotSpot 가비지 수집기는 이러한 작업의 효율성을 높이기 위해 다양한 기술을 사용합니다.

  • 자원을 효율적으로 사용하기 위해 힙영역을 세대별로 관리하여 Garbage가 될 가능성이 높은 영역을 집중관리합니다.
  • 여러 스레드를 사용하여 작업을 병렬화하거나 애플리케이션과 동시에 백그라운드에서 일부 장기 실행 작업을 수행합니다.

GC 종류에 관심을 가져야 하는 이유

대규모 어플리케이션, 특히 대량의 데이터 많은 스레드 및 높은 트랜잭션 수를 가진 애플리케이션의 경우 GC의 영향을 많이 받습니다.
https://docs.oracle.com/en/java/javase/20/gctuning/introduction-garbage-collection-tuning.html#GUID-A48F272E-A6C1-45A0-9A8B-6D5790EB454C


해당 그래프에서 y축은 처리량, x축은 프로세스 수를 나타냅니다.

그래프 선에 적힌 퍼센트는 가지비 수집시간 비율을 나타냅니다.

빨간색 시스템의 경우 가비지 수집시간이 시스템의 1%를 차지합니다. 해당 시스템은 프로세서가 0~5개 사이일때는 처리량이 1에 가깝지만, 약 30개가까이 시스템을 확장했을 경우 처리량이 20%가까히 줄어들게 됩니다.

 

해당 그래프는 확장된 대규모 시스템에서 가지비 수집시간에 의한 처리량감소가 치명적일 수 있음을 보여줍니다.

소규모 시스템에서 개발할 때 무시할 수 있는 처리량 문제가 대규모 시스템으로 확장할 때 주요 병목 현상이 될 수 있음을 보여줍니다. 그러나 이러한 병목 현상을 줄이는 작은 개선만으로도 성능이 크게 향상될 수 있습니다. 충분히 큰 시스템의 경우 올바른 가비지 수집기를 선택하고 필요한 경우 조정하는 것이 좋습니다.

 

Garbage Collector의 세대별 영역관리

java의 heap영역은 young영역과 old영역으로 나뉩니다.

https://medium.com/@khurshidbek-bakhromjonov/java-memory-management-understanding-the-jvm-heap-method-area-stack-24a4d4fa2363

 

세대별로 영역을 나누게 된 이유는 약한 세대 가설(week generational hypothesis)에 기반합니다.

약한 세대 가설은 대부분의 객체가 짧은 수명을 가진다는 특정을 말합니다.  조금 더 '세대' 관점에 맞춰 설명하자면, 대부분의 객체는 '어릴 때 죽는다' 라는 특성을 가지고 있음을 말합니다.

 

Heap 세대별 영역에서 객체의 움직임

대부분의 객체는 처음에 eden 영역에 할당됩니다. 가비지 컬렉션은 세대가 가득차면 각 세대에서 발생합니다. Young Generation을 대상으로 진행되는 가비지 컬렉션을 YoungGC(Minor GC) 이라고 지칭합니다. 이때 다른 세대의 쓰레기는 회수되지 않습니다. Young Generation에서 살아남은 객체는 Survivor 영역으로 이동합니다. 간혹 edensurvivor영역에 공간이 부족할 경우 old 영역으로 넘어가기도 합니다. Survivor영역으로 넘어온 뒤에는 gc를 진행할 때마다 S0(survivor0)S1(survivor1)을 번갈아가며 이동합니다. 이 때 GC설정값에 따라 이동횟수가 특정 값을 넘게되면 old영역으로 이동하게 됩니다. 그 후 Old Generation을 대상으로 하는 가비지 컬렉션을 FullGC(major GC)라고 지칭합니다.

 

이렇게 heap 영역을 나누어 GC 가능성이 높은 객체를 세대라는 개념으로 관리하고 GC의 효율성을 높히는 구조를 가지고 있습니다.

 

 

Garbage Collector의 종류

 

Serial GC

  • - GC를 단일 쓰레드로 실행합니다.
  • - 쓰레드간 GC 오버헤드가 발생하지 않습니다.
  • - 웹서비스에는 적합하지 않고, 작은 장비, 단일프로세서에는 적합합니다.

Parallel GC

  •  GC를 여러 쓰레드로 실행합니다.

Shenandoah GC

  • Red hat 개발
  • openJDK 12에 추가
  • 중단시간이 거의 없습니다.
  • 실시간 compaction(메모리정리)
  • 높은 cpu 사용량

Z-GC

  • 확장 가능한 빠른 가비지 컬렉터
  • 적용가능한 힙 크기 8MB - 16TB
  • 최대 대기시간은 밀리초 단위로 목표

Garbage-First(G1)

  • 기존 힙영역간에 객체 이동시 복제하는 비용이 들었는데, 바둑팍처럼 메모리영역을 나누어 복제하지 않고 바둑판각각에 메모리 영역을 부여하고 필요에 따라 영역을 변경합니다.
  • 가장 많이 사용되는 GC입니다.
  • java 9이상부터는 G1을 사용하면 GC 종류로 인한 이슈는 거의 생기지 않는다고 합니다.

https://thinkground.studio/2020/11/07/%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-gc-%EB%82%B4%EC%9A%A9%EA%B3%BC-g1gc-garbage-first-garbage-collector-%EB%82%B4%EC%9A%A9/

 

 

 

jvm 메모리크기는 얼만큼 설정해야 할까?

메모리크가가 커지면 GC 소요시간이 길어집니다. 반대로 메모리가 작아지면 OOM 위험이 커집니다. 즉 서비스 특성에 따라 최적화된 선택을 해야합니다. 보통 2기가로 설정하거나 많이 필요하면 4기가 추천한다고 합니다.

Garbage Collector 트러블 슈팅

Full GC는 자주일어나지 않기 때문에 old영역에 대한 메모리 사용량은 계속해서 늘어납니다. 이는 정상적인 현상이며 문제식별은 Full GC가 끝난 직후 메모리 상황을 봐야합니다. GC후에도 메모리 사용량이 많다면, 서비스에 이상이 있을 수 있으며 이때 Heap dump를 통해 원인을 파악해야합니다.

GC 와 TimeOut 그리고 CPU

timeout 시간은 항상 GC시간보다 많게 설정해야 합니다. GC시간보다 time out이 적다면 종종 GC로 인해 정상적인 요청이 time out으로 실패할 가능성이 있습니다. 때문에 GC시간과 timeout시간을 적절히 분배해야 안정적인 서비스를 제공할 수 있습니다. 

GC시간을 줄이기 위해 높은 성능을 나타내는 GC를 선택한다면 GC소요시간은 줄지만 CPU사용량은 올라갑니다. 만약 서비스의 CPU사용량이 80%정도 유지되는 서비스에 높은 CPU사용량을 가지는 GC가 실행된다면 CPU사용량이 100%가 되어 오히려 성능적으로 안좋은 결과를 가져올 수 있습니다.

'java' 카테고리의 다른 글

성능이란? 성능은 왜 중요할까  (0) 2024.02.20
JAVA JDK21 가상스레드란? 배경부터 테스트까지  (1) 2024.01.29