JVM GC 상태 진단
이 글에서 다루는 내용
JVM 상태 진단, systemd 서비스 옵션 튜닝까지의 트러블슈팅 과정을 정리한다.
JVM GC 상태 진단
jstat -gcutil 918054 2000 5
jstat -gcutil 2198075 2000 5
| 항목 | blog-server | word-mov |
|---|---|---|
| Metaspace | 99.48% | 99.41% |
| Old Gen | 76.48% | 84.59% |
| CGC 횟수 | 190 | 1040 |
두 서버 모두 Metaspace가 99% 가까이 꽉 차있었고, word-mov의 경우 CGC(Concurrent GC)가 1040번 발생해 blog 서버의 5배에 달했다.
왜 Metaspace가 꽉 차면 발열이 생기나
CPU 점유율이 낮아 보여도 JVM은 백그라운드에서 지속적으로 GC를 수행한다. Metaspace가 한계에 가까워지면 다음과 같은 루프가 반복된다.
Metaspace 99% 도달
→ JVM "공간 부족!"
→ Concurrent GC 트리거
→ 조금 비워짐
→ 다시 차오름
→ 또 CGC 트리거
→ 반복...
이 과정에서 CPU에 지속적인 소량의 부하가 걸리고, 그게 온도를 끌어올리고, 팬을 계속 돌게 만든다. top으로는 순간 점유율이 낮게 보여도 실제로는 쉬지 않고 일하고 있는 것이다.
해결 — systemd 서비스에 JVM 옵션 추가
sudo nano /etc/systemd/system/word-mov.service
기존 ExecStart:
ExecStart=/usr/bin/java -jar /srv/ss/app/word-mov.jar --spring.profiles.active=prod
수정 후:
ExecStart=/usr/bin/java \
-Xms128m \
-Xmx512m \
-XX:MetaspaceSize=128m \
-XX:MaxMetaspaceSize=256m \
-XX:+UseG1GC \
-XX:G1HeapRegionSize=4m \
-jar /srv/ss/app/word-mov.jar --spring.profiles.active=prod
각 옵션의 역할:
| 옵션 | 역할 |
|---|---|
-Xms128m |
JVM 시작 힙 크기 128MB로 고정 |
-Xmx512m |
힙 최대 512MB로 제한 |
-XX:MetaspaceSize=128m |
Metaspace 초기값 명시 |
-XX:MaxMetaspaceSize=256m |
Metaspace 무한 증가 방지 |
-XX:+UseG1GC |
G1GC로 교체하여 GC 효율 개선 |
-XX:G1HeapRegionSize=4m |
ARM 환경에 맞는 리전 크기 설정 |
적용:
sudo systemctl daemon-reload
sudo systemctl restart word-mov
예상 효과
| 이전 | 이후 | |
|---|---|---|
| word-mov 메모리 | ~900MB | ~512MB |
| blog-server 메모리 | ~617MB | ~256MB |
| 전체 JVM 메모리 | ~1,516MB | ~768MB |
| CGC 빈도 | 1040회+ | 대폭 감소 예상 |
메모리 자체를 줄이는 것보다 Metaspace에 명확한 상한선을 설정하는 것이 핵심이다. 제한이 없으면 JVM은 Metaspace를 계속 늘리다가 99%가 될 때마다 GC 폭탄을 터뜨린다.