Programing/JAVA

Java GC Gargabe Collection 알짜만 빼먹기 / 알고리즘 / 종류 / 모니터링 VisualVM

리커니 2022. 5. 12. 20:26
반응형

Java GC Garbage Collection 알짜만 빼먹기 / 알고리즘 / 종류 / 모니터링 VisualVM

 

이번 포스팅에서는 제가 알고있는 Garbage Collection에 대해 정리하는 시간을 갖어볼까 합니다.

 

GC (Garbage Collection) 란?

GC는 JVM에서 자동으로 Heap 메모리를 관리하는 기능입니다. 


GC 의  알고리즘

GC-Root로 부터 참조되는 객체와 그 Reachable 객체가 참조하는 객체를 찾아서 Marking 하고

Marking 과정이 끝나면 Marking되지 않은 UnReachable 객체를 메모리에서 해제(Sweep),

살아남은 영역을 조각 모음(Compact) 하는 Mark and Sweep Compact 알고리즘을 기본으로 사용합니다.

 

 

Mark and Sweep

Mark and Sweep Compact

Mark Summary Compact (추후 추가)

 


GC-Root 대상

Heap 영역 내 다른 객체 참조가 있을 경우

Stack 영역의 지역변수, 파라메터에 의한 참조가 있을 경우

Method 영역의 static 변수에 의한 참조가 있을 경우

JNI에 의해 생성된 객체에 의한 참조가 있을 경우

 


GC 실행 과정

GC의 실행 과정을 보기에 앞서 JVM Heap 메모리 영역에 대해서 알아보겠습니다.

Heap 영영은 Eden, Service0, Service1 영역으로 구성되는 Young Generation 영역과

Old Generation 영역으로 구성됩니다.

Young Generation 영역에서 발생하는 GC 를 MinorGC,

Old Generation 영역에서 발생하는 GC 를 MajorGC라고 합니다.

 

aljjabaegi.tistory.com GC Heap

  

객체가 인스턴스화 되면 제일 처음 Eden 영역에 생성되고 Eden 영역이 한계치를 초과하면

MinorGC가 발생하여 살아남은 객체들이 Service0 영역으로 이동됩니다. 앞의 과정이 반복되어

Service0 영역의 한계치를 초과하면 다시 살아남은 객체들은 Service1 영역으로 이동됩니다.

Service 영역간 살아남은 객체들의 이동이 반복되며 Service 영역 중 한 영역은 항상 비어있게 됩니다.  

이 처럼 MinorGC 가 발생하면 살아남은 객체들은 영역을 이동하면서 age-bit 가 1씩 증가하게 되는데

MaxTenuringThreshold 설정 값을 초과하게 되면 Old Generation 영역으로 이동하고 이를 Promotion이라 합니다.

 

간단하게 이 과정을 VisualVM으로 확인해 보도록 하겠습니다.

지속적으로 큰 크기의 참조 객체를 생성하고 List에 add하여

java.lang.OutOfMemoryError: Java heap space Exception을 발생시키는 프로그램을 동작하게 한 후 Visual GC 를 모니터링한 영상입니다. 

영상을 보시면 위의 과정이 더 쉽게 이해될 것이라 생각됩니다.

 

aljjabaegi.tisory.com GC 실행 과정


 

Stop-the-World

GC가 발생하게 되면 GC를 실행하는 Thread를 제외한 모든 Application의 Thread가

GC 종료 전까지 일시정지 하게 되고 이를 Stop-the-World 라고 합니다.

Application의 모든 Thread가 GC 종료 전까지 일시정지 되기 때문에 Application의 성능에 많은 영향을 끼치게 되고 MinorGC 보다는 실행속도가 느린 MajorGC 에서 문제가 발생하게 됩니다.

MajorGC의 Stop-the-World 시간을 줄이기 위해 개선된 알고리즘이 나오고 있고,

보통의 경우 GC 튜닝은 이 MajorGC의 Stop-the-World 시간을 단축하는 작업입니다.

 


 

GC의 종류

GC의 종류에는 Serial GC, Parallel GC, Parallel Old GC, CMS GC, G1 GC, Z GC 가 있습니다.

Z GC에 대해서는 추후에 추가하도록 하고 G1 GC 까지 알아보도록 하겠습니다.

 

 

Serial GC 는 예전 싱글코어에서 사용된 GC로 Single Thread로 동작하기 Stop-the-World 시간이 길고 느립니다.

Mark and Sweep Compact 알고리즘을 사용합니다.


Parallel GC는 Java8의 Default GC로 Minor GC 에서만 Multi Thread로 동작합니다. Serial GC에 비해 속도가 많이 개선되었고 Mark and Sweep Compact 알고리즘을 사용합니다.


Parallel old GC 는 Parallel GC와 달리 Major GC에서도 Multi Thread로 동작합니다.

Minor GC는 Mark and Sweep Compact, Major GC는 Mark Summary Compact 알고리즘을 사용합니다.


CMS (Concurrent Mark and Sweep) GC 는 Mark and Sweep 을 개선하여 Stop-the-World를

최소화하기 위한 방식입니다. Stop-the-World 시간이 짧지만 다른 GC보다 CPU와 메모리를 많이 사용하고  더이상 메모리 할당이 어려울 때까지 미룬 Compaction이 실행되면 다른 방식보다 더 긴 Stop-the-World가 발생하게 됩니다. Java14 부터 사용이 중지되었습니다.

 

Initial Mark → Concurrent Mark → Remark → Concurrent Sweep →→→→→ (Compact) 의 순서로 실행됩니다.

 

Initial Mark : Single Thread로 동작하며 GC-Root에서 가장 가까운 객체 중 살아 있는 객체와 Young Generation에 살아있는 객체가 참조하는 객체를 Marking 합니다. 짧은 Stop-the-World가 발생.

Concurrent Mark : Initial Mark 과정에서 Marking 된 객체에서 참조하는 객체들을 따라가면서 확인합니다.

이 과정은 Application Thread와 동시에 진행됩니다. Stop-the-World가 발생하지 않음.

Remark  : Multi Thread로 동작하며 Concurrent Mark 과정에서 추가적으로 참조되거나 끊긴 객체를 확인합니다.

Stop-the-World가 발생.

Concurrent Sweep : 참조되지 않는 객체를 메모리에서 해제합니다. Application Thread 와 동시에 진행됩니다.

Compact : 최대한 미루다 더이상 메모리 할당이 어려울 경우 실행됩니다. 긴 Stop-the-World 발생.


G1(Garbage First) GC 는 다른 GC 방식과는 다르게 Heap 영역 전체를 1~32MB 의 동일한 사이즈의 지역(Region)들로 나누고 각 지역이 Eden, Survivor, Old,  Available/Unused 역할을 수행합니다. Humongous는 큰 객체 할당을 위한 지역입니다.

Garbage First 라는 이름에서 알 수 있듯이 Garbage가 꽉찬 지역을 우선적으로 GC가 동작하게됩니다.

현재 GC 중 Stop-the-World 시간이 제일 짧습니다.

Multi core CPU, 대용량 메모리 시스템을 위한 GC 입니다.

 

Eden : 다른 GC의 Eden 역학과 같은 역할, 새로 생긴 객체들이 할당.

Survivor : 살아있는 객체들이 할당.

Old : 오래 살아있는 객체들이 할당.

Humongous : Region 크기의 50%가 넘는 큰 객체들을 할당.

Available/Unused : 아직 사용되지 않는 영역.

 

aljjabaegi.tistory.com G1GC Heap Memory

 

G1 Young GC

이전 GC들과 마찮가지로 객체는 제일 처음 Eden 지역에 생성되고 MinorGC를 거쳐 살아남은 객체들은

Servivor 지역으로 이동된 후 Eden 지역은 Available/Unused로 변경됩니다.

 

G1 Old GC

Initial Mark → Root Region Scan → Concurrnet Mark → Remark → Clean up → Copy 의 순서로 실행됩니다.

 

aljjabaegi.tistory.com G1 Old GC

 

Initial Mark : Old 지역의 객체들이 참조하는 객체를 Survivor 지역에서 찾습니다. Stop-the-World 발생.

Root Region Scan : Initial Mark 과정에서 살아남은 객체(Old지역의 객체로부터 참조되거나 참조된 객체로 Mark 된)가 있는 Survivor 지역을 스캔합니다.  Stop-the-World 없이 Application Thread와 동시에 실행됩니다.

Concurrent Mark : 전체 Heap 에서 GC의 대상이 될 지역을 Mark 합니다.

Remark : Concurrent Mark 과정에서 GC의 대상이라고 판단된 지역 메모리에서 해제하고 Available/Unused 로 변경합니다. Stop-the-World 발생.

Clean up : 살아남은 객체가 가장 적은 지역의 미사용 객체를 제거합니다.  Stop-the-World 발생.

Copy : Clean up 과정에서 완전히 비워지지 않은 지역의 살아남은 객체들을 Available/Unused 지역으로 복사하여 Compaction을 수행합니다.  Stop-the-World 발생.

 

위에서 실행했던 프로그램의 GC를 G1 GC로 변경했을 경우 Visual GC를 확인해보겠습니다. 

메모리 사용 부분에서 차이가 나는 것만 확인하도록 합니다.

 

aljjabaegi.tisory.com G1 GC 실행 과정

 

G1 GC의 옵션에 대해서 알아보겠습니다.

GC의 옵션 값 만큼 Heap Size 설정도 GC 튜닝에서 중요한 부분입니다. 크게 설정하면 Stop-the-World 가 보다 덜 발생하지만 오래 걸리게 되고, 작게 설정하면 시간은 줄지만 자주 발생하게 됩니다.  GC를 모니터링 하면서 적절한 사이즈를 설정해야 합니다.

 

최대 Heap Size 설정 : -Xmx1024m

최소 Heap Size 설정 : -Xms512m

 

Options Explanation
-XX:+UseG1GC G1GC 사용.
-XX:G1HeapRegionSize=n G1 영역의 크기를 설정합니다. 값은 2의 거듭제곱이 되며 범위는 1MB에서 32MB입니다. 목표는 최소 Java 힙 크기를 기준으로 약 2048개의 영역을 갖는 것입니다.
-XX:MaxGCPauseMillis=200 원하는 최대 일시 정지 시간에 대한 목표 값을 설정합니다. 기본값은 200밀리초입니다. 지정된 값이 힙 크기에 맞지 않습니다.
-XX:G1NewSizePercent=5 젊은 세대 크기의 최소값으로 사용할 힙의 백분율을 설정합니다. 기본값은 Java 힙의 5%입니다. 이것은 실험적인 플래그입니다. 예를 보려면 " 실험적 VM 플래그를 잠금 해제하는 방법 "을 참조하세요 . 이 설정이 설정을 대체합니다 -XX:DefaultMinNewGenPercent. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.
-XX:G1MaxNewSizePercent=60 젊은 세대 크기의 최대값으로 사용할 힙 크기의 백분율을 설정합니다. 기본값은 Java 힙의 60%입니다. 이것은 실험적인 플래그입니다. 예를 보려면 " 실험적 VM 플래그를 잠금 해제하는 방법 "을 참조하세요 . 이 설정이 설정을 대체합니다 -XX:DefaultMaxNewGenPercent. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.
-XX:ParallelGCThreads=n STW 작업자 스레드의 값을 설정합니다. n 값을 논리 프로세서 수로 설정합니다. 값은 n최대 값 8까지의 논리 프로세서 수와 동일합니다.
8개 이상의 논리 프로세서가 있는 경우 값을 n논리 프로세서의 약 5/8로 설정합니다. 이는 값이 n논리 프로세서의 약 5/16일 수 있는 더 큰 SPARC 시스템을 제외하고 대부분의 경우에 작동합니다.
-XX:ConcGCThreads=n 병렬 마킹 스레드 수를 설정합니다. n병렬 가비지 수집 스레드 수의 약 1/4로 설정 합니다( ParallelGCThreads).
-XX:InitiatingHeapOccupancyPercent=45 표시 주기를 트리거하는 Java 힙 점유 임계값을 설정합니다. 기본 점유는 전체 Java 힙의 45%입니다.
-XX:G1MixedGCLiveThresholdPercent=65 혼합 가비지 수집 주기에 포함될 이전 영역의 점유 임계값을 설정합니다. 기본 점유율은 65%입니다. 이것은 실험적인 플래그입니다. 예를 보려면 " 실험적 VM 플래그를 잠금 해제하는 방법 "을 참조하세요 . 이 설정이 설정을 대체합니다 -XX:G1OldCSetRegionLiveThresholdPercent. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.
-XX:G1HeapWastePercent=10 낭비할 힙의 백분율을 설정합니다. Java HotSpot VM은 회수 가능한 백분율이 힙 낭비 백분율보다 작은 경우 혼합 가비지 수집 주기를 시작하지 않습니다. 기본값은 10%입니다. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.
-XX:G1MixedGCCountTarget=8 G1MixedGCLIveThresholdPercent최대 라이브 데이터 가 있는 이전 영역을 수집하기 위해 표시 주기 후 혼합 가비지 수집의 대상 수를 설정합니다 . 기본값은 8개의 혼합 가비지 컬렉션입니다. 혼합 컬렉션의 목표는 이 목표 수 내에 있는 것입니다. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.
-XX:G1OldCSetRegionThresholdPercent=10 혼합 가비지 수집 주기 동안 수집할 이전 영역 수의 상한을 설정합니다. 기본값은 Java 힙의 10%입니다. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.
-XX:G1ReservePercent=10 공간 오버플로의 위험을 줄이기 위해 여유 공간을 유지할 예약 메모리의 백분율을 설정합니다. 기본값은 10%입니다. 백분율을 늘리거나 줄일 때 동일한 양으로 전체 Java 힙을 조정해야 합니다. 이 설정은 Java HotSpot VM, 빌드 23에서 사용할 수 없습니다.

참고 URL : https://www.oracle.com/technical-resources/articles/java/g1gc.html

 

지금까지 제가 알고 있는 GC에 대한 내용을 정리하였습니다.

부족한 부분이나 잘못된 부분이 있다면 댓글 부탁드립니다.

 

참고로 Visual VM의 설치는 아래의 Link를 참고하세요!

 

Link : https://aljjabaegi.tistory.com/559

 

JVM 모니터링 Java 메모리 CPU 모니터링 프로그램 VisualVM

JVM 모니터링 Java 메모리 CPU 모니터링 프로그램 VisualVM Java application 의 메모리와 CPU 사용량을 모니터링 해야 할 때가 있습니다. Heap Memory 나 PermGen Memory Error 가 발생을 하거나 메모리..

aljjabaegi.tistory.com

 

 

 

반응형