๐ป [ko] Heap Dumps: How to Capture and Analyze in Spring Boot Applications
โ@Backend OO๋, ํ ๋คํ ๋ฐ ์ค ์์ฃ ?โ
When servers experience unexplained crashes or performance drops, often due to memory leaks, capturing heap dumps is essential for diagnosis. This guide explains how to generate heap dumps in Spring Boot applications.
๋ค์ด๊ฐ๋ฉฐโฆ
์๋ฒ๊ฐ ์ ์ ์๋ ์ด์ ๋ก ๊ฐ์๊ธฐ ์ฃฝ๊ฑฐ๋ ์ฑ๋ฅ์ด ์ ํ๋๋ ๊ฒฝ์ฐ๊ฐ ์ข ์ข ์๋๋ฐ,
๋ณดํต heap memory ๋ด์์ memory leak ์ด ์์ธ์ธ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
๋ณด๋ค ์ ๋ฐํ๊ฒ ๋ถ์ํด์ ํด๊ฒฐํ๋ ค๋ฉด heap dump ๋ฅผ ๋ ์ ํ์ธํด๋ณด์์ผ ํ๋๋ฐ,
์ด์ ์ ๊ด๋ จ ๊ฒฝํ์ด ์๋ค๋ฉด heapdump ๋ฅผ ๋จ๋ค.
๋ผ๋ ๋ง ์์ฒด๊ฐ ์์ํ ์ ๋ฐ์ ์๋ค.
๋ณธ ํฌ์คํ
์์ heapdump ๋ฅผ ๋ ๋ณด๋ ํ์
๋ฅผ ํ ์ฌ์ดํด ํด๋ณด๋ฉด์.. ๊ฐ๋ณ๊ฒ ๋๋์ ์ก์๋ณด๋๋ก ํ์.
๋นํฉํ์ง ์์ ๋ฏธ๋์ ์์ ์ ์ํด..!
์ฌ์ด๋ ํ๋ก์ ํธ๋ก ํ๋ฒ ์ค์ตํด๋ณด์~!
ํ์ฌ ์งํ์ค์ธ ์ฌ์ด๋ ํ๋ก์ ํธ์์ ์ฌ์ฉ๋๋ Spring boot application ์ jib tool ์ ์ด์ฉํ์ฌ, docker container ํ๊ฒฝ์์ ๋ฐฐํฌํ๊ณ ์๋ค.
์ด์ ๊ฐ์ ํ๊ฒฝ์์ heap dump ๋ ์ด๋ป๊ฒ ์์ฑํ๋ ์ง ์ง๊ธ ๋ฐ๋ก ๊ฐ๋จํ๊ฒ ์์๋ด ์๋ค!~
Heap-dump
heap dump ๋ถ์์ ํตํด Java application ์ ์ฑ๋ฅ ๋ถ์์ ์ฝ๊ฒ ํ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ heap dump ์ด๋, heap memory ์ ๋ํ snapshot ์ผ๋ก
memory leak ์ด๋ ๊ธฐํ ๋ค๋ฅธ ๋ฌธ์ ๋ฅผ ์ง๋จํ๋ ๋ฐ์ ํฐ ๋จ์๋ฅผ ์ ๊ณตํด์ค๋๋ค.
์ด์ ์ ์ฌ์ด๋ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ์๊พธ ec2 ์๋ฒ๊ฐ OOM (OutOfMemory) ์๋ฌ ๋ก๊ทธ๋ฅผ ๋์ฐ๋ฉฐ ์ฃฝ๋ ๊ฒฝํ์ ํ์๋๋ฐ
๊ทธ ๋น์์ heap dump ์ ๋ํด์ ์๊ณ ์์๋๋ผ๋ฉด
์กฐ๊ธ ๋ ์ฝ๊ฒ ๋ฌธ์ ๋ฅผ ์ง๋จํ๊ณ ํด๊ฒฐํ ์ ์์ง ์์์๊น.. ํ๋ ์์ฌ์์ด ์์ต๋๋ค.
์ฌํผ, heap dump ๋ฅผ ํ์ฉํ๋ฉด memory leak ์ ์ํ ์๋ฒ ์ฅ์ ๊ฐ ๋ฐ์ํด๋ ํฌ๊ฒ ๋นํฉํ์ง ์๊ณ ์์ธ์ ๋ถ์ํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก OutOfMemory ๊ฐ ๋ฐ์ํ๋ฉด, ์ฌ์ ์ ์ง์ ํด๋ path ์ heap dump ํ์ผ์ ์์ฑํ๋๋ก ํด๋๋ ๊ฒ์ด ๋ง์ ํธํ ๋ฐฉ๋ฒ์ธ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ฅผ ์ํด์ JVM option ์ ์ฃผ๋ฉด ๋๋๋ฐ,
์ฌ์ด๋ ํ๋ก์ ํธ์์๋ jib ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฏ๋ก
์๋์ ๊ฐ์ด jvmFlag ๋ฅผ ์ง์ ํด์, OOM ๋ฐ์ ์ ์ง์ ๋ ๊ฒฝ๋ก์ heapdump ๊ฐ ์์ฑ๋ ์ ์๋๋ก ํ ์ ์์ต๋๋ค.
๊ฐ์์ ํ๋ก์ ํธ ํ๊ฒฝ์ ๋ง์ถฐ์ ์ค์ ํด์ผ ํ๋ฏ๋ก ์ฐธ๊ณ ์ฉ์ผ๋ก๋ง ๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค!
jib {
from {
image = "openjdk:17"
}
to {
image = "your docker image"
tags = ["latest"]
}
container {
...
jvmFlags = [ ... your jvm option ... , "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/your-path/your-heap-dump-file-name.hprof"]
...
}
}
docker file ์ ์ฌ์ฉํ๋ค๋ฉด, ์๋์ ๊ฐ์ด ํด์ค ์ ์๊ฒ ๋ค์!
FROM openjdk:17
...
CMD ["java", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/path/to/heapdump.hprof", "-jar", "myapp.jar"]
์ฌ์ค ์ด๋ ๊ฒํ๋ฉด heapdump ์ ํ์ผ๊ฒฝ๋ก๊ฐ ์ธ์ ๋ ๋์ผํ๋ฏ๋ก, overwrite ๋๋ค๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
๊ทธ๋์ ๋ณดํต์ ๋ ์ง๋ฅผ ์ด์ฉํด์, OOM ์ด ๋ฐ์ํ ์์ ์ ๋ ์ง๋ก heapdump ํ์ผ์ ์ด๋ฆ์ ์ง์ ํด์
heapdump ํ์ผ๋ค์ ํ์คํ ๋ฆฌ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ ๊ฐ์ต๋๋ค.
ํ์ง๋ง ์ ๊ฐ ์ด์ฉ์ค์ธ ์ฌ์ด๋ ํ๋ก์ ํธ์ ec2 ์ฌ์์ t2.micro ์ด๊ณ ..
heapdump ํ์ผ์ ์ฉ๋์ด ์์ง ์์ ํธ์ด๋ฏ๋ก
๋งค๋ฒ overwrite ๋๋๋ก ์ค์ ํด๋์์ต๋๋ค.
์ฌํผ, ์์ ๊ฐ์ด JVM ์ต์ ์ ์ฃผ๋ฉด
OOM ์ด ๋ฐ์ํ๋ฉด ์ฐ๋ฆฌ๊ฐ ์ง์ ํด๋ ๊ฒฝ๋ก์ heapdump ํ์ผ์ด ์๋์ผ๋ก ์์ฑ๋ฉ๋๋ค.
heapdump ํ์ผ์ ๋ณดํต eclips memory analysis tool(MAT) ๋ฅผ ์ด์ฉํด์ ์๊ฐ์ ์ผ๋ก ๋ถ์ํ ์ ์์ต๋๋ค.
Heap dump ์๋์ผ๋ก ์์ฑํ๊ธฐ
์์ ๊ฐ์ด jvm option ์ ์ฃผ๋ฉด OOM ๋ฐ์ ์ heap dump ํ์ผ์ด ์ง์ ๋ ๊ฒฝ๋ก์ ์์ฑ๋ฉ๋๋ค.
๋ง์ฝ, ์๋์ผ๋ก ์์ฑํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊น์?
๋ณดํต jmap
์ ์ด์ฉํ์ฌ heap dump ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, java app ์ pid ๊ฐ 1111 ์ด๋ผ๋ฉด ์๋์ ๊ฐ์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ฌ heapdump file ์ ์์ฑํ ์ ์์ต๋๋ค.
jmap -dump:live,format=b,file=heapdump.hprof 1111
jmap
๋ช ๋ น์ด๋ ์คํ์ค์ธ java process ์ ๋ถํ๋ฅผ ์ค ์ ์๊ธฐ ๋๋ฌธ์, production ์์๋ ์ฃผ์ํด์ฃผ์ธ์!- ๋ง์ฝ
jmap
๋ช ๋ น์ด๋ฅผ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ์๋jcmd
๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ heapdump ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.jcmd 1111 GC.heap_dump heapdump.hprof
- ํ์ธํด๋ณด๋
jmap
๋ณด๋คjcmd
๊ฐ ์กฐ๊ธ ๋ ์ฑ๋ฅ์ ์ผ๋ก ์ฐ์ํ๋ค๊ณ ํ๋,jcmd
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ๊ฒ ๊ฐ๋ค์!
java app ์ด docker container ๋ด์์ ๋์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ ์ผ๋ฐ์ ์ผํ ๋ฐ,
์ด๋๋ ํด๋น container ๋ด๋ถ์ ๋จผ์ ์ ๊ทผํด์ผ๊ฒ ์ฃ ..!
docker ps
๋ช ๋ น์ด๋ฅผ ํตํด ์คํ์ค์ธ Spring app ์ container id ๋ฅผ ์์๋ธ ๋ค์
docker exec -it [your-container-id] bash
๋ช ๋ น์ด๋ฅผ ํตํด ์ปจํ ์ด๋์ bash ํฐ๋ฏธ๋๋ก ๋ถ์ด์ค์๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ์ปจํ ์ด๋์์ ๋์ํ๊ณ ์๋ java app ์ pid ๋ฅผ ์์๋ด์ผ ํ๋๋ฐ์,
ps -ef | grep java
๋ช ๋ น์ด๋ฅผ ํตํด heapdump ๋ฅผ ๋จ๊ณ ์ ํ๋ java app ์ pid ๋ฅผ ์ป์ด๋ ์๋ค.
๋ง์ฝ ํด๋น ์ปจํ ์ด๋์์ ps ๋ช ๋ น์ด๋ฅผ ์ง์ํ์ง ์๋๋ค๋ฉด (์ฌ์ค ๋๋ถ๋ถ baseimage ๊ฐ jdk ์ผํ ๋.. ์ง์ ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ ๊ฒ ๊ฐ๋ค์.)
jps -l
๋ช ๋ น์ด๋ฅผ ํตํด ์คํ์ค์ธ java app ์ pid ๋ฆฌ์คํธ๋ฅผ ์กฐํํ ์ ์์ต๋๋ค.
container to ec2-host
๋์ปค ์ปจํ ์ด๋ ๋ด์ ์์ฑ๋ heapdump file ์ host ๋ก ์ฎ๊ฒจ์ผ๊ฒ ์ฃ !?
์ด๋ฅผ ์ํด์๋ ์๋์ ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
docker cp mycontainer:/heapdump.hprof to-directory
์๋ฅผ ๋ค์ด
docker cp mycontainer:/heapdump.hprof .
๋ผ๊ณ ์
๋ ฅํ ๋ค์ exit
๋ฅผ ํตํด container ๋ฐ๊นฅ์ผ๋ก ๋๊ฐ๊ณ root directory ๋ก ๊ฐ๋ณด๋ฉด heapdump.hprof
ํ์ผ์ด ์์ ๊ฒ๋๋ค!
ec2-host to local
์ด์ ec2 ์ ์๋ heapdump ํ์ผ์ ๋ก์ปฌ ์ปดํจํฐ๋ก ์ฎ๊ฒจ์ฃผ์ด์ผํฉ๋๋ค.
๋ณดํต scp ๋ช ๋ น์ด๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ ๊ฒ ๊ฐ์ต๋๋ค.
scp -i [your-pem-key-file-path] [your-ec2-user-name]@[your-ec2-ip-address]:[heapdump-file-path] [to-file-path]
๋ง์ฝ, ์ ๋ช ๋ น์ด๋ฅผ ์คํํ๋ ๋์ค์
scp: remote open "/heapdump.hprof": Permission denied
์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค๋ฉด
heapdump.hprof ์ ๊ถํ์ ํ์ธํ๊ณ ํ์ ์ ์กฐ์ ํด์ค์๋ค.
์์๋ก,
์๋ ๋ช ๋ น์ด๋ฅผ ํตํด heapdump.hprof ์ ๋ํ rw ๊ถํ์ ๋ชจ๋ ์ฌ์ฉ์๋ก๋ถํฐ ํ์ฉํ ์ ์์ต๋๋ค.
sudo chmod 644 /path/to/heapdump.hprof
์ด์ ๋ค์ ํ์ผ ์ ์ก์ ํ๋ฉด ์ํํ๊ฒ ๋ ๊ฒ ์ ๋๋ค ใ _ใ
์, ๊ทธ๋ฐ๋ฐ ํ๋ ๊ถ๊ธ์ฆ์ด ์๊ธธ ์ ์์ ๊ฒ ๊ฐ์์.
์ง๊ธ ์ฐ๋ฆฌ๋ ๋์ปค ์ปจํ ์ด๋ ๋ด๋ถ์ ์์ฑ๋ ํ ๋คํ๋ฅผ ๋ก์ปฌ์ ์ฎ๊ธฐ๋ ค๊ณ ec2 ๋ฅผ ์ค๊ฐ ๋ค๋ฆฌ๋ก ์ฌ์ฉํ๊ณ ์์ฃ ..?
๋์ปค ์ปจํ ์ด๋์์ ๋ก์ปฌ๋ก ๋ฐ๋ก ์ ์กํ์ง ์๊ณ , ์ ec2 ์ ๊ฐ๋ค๊ฐ ๋ก์ปฌ๋ก ์ ์กํ๊ณ ์๋๊ฑธ๊น์?
๋์ปค์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์์น์ด ๋ฐ๋ก ๊ฒฉ๋ฆฌ์ฑ์ด์ฃ !?
host system ์ ๊ฑฐ์ณ์ local ์ผ๋ก ํ์ผ์ ์ ์กํ๋ ๊ฒ์ ๋์ปค์ ๊ธฐ๋ณธ ์์น์ธ ๊ฒฉ๋ฆฌ์ฑ์ ์ค์ํ๋ฉด์๋,
๋ณด์์ ์ผ๋ก ์์ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๋์ปค ์ปจํ ์ด๋๋ฅผ ์ง์ ์ ์ผ๋ก ์ธ๋ถ ๋คํธ์ํฌ์ ๋ ธ์ถ์ํค๋ ๊ฒ์ ๊ฒฉ๋ฆฌ์ฑ๋ ์๋ฐฐํ๊ณ , ๋ณด์์ ์ผ๋ก ์ข์ง ์์์.
eclipse-MAT
์ด์ ์์ฑ๋ heapdump ํ์ผ์ eclipse-MAT ์ผ๋ก ์ด์ด๋ด ์๋ค~!
Java heapdump ๋ถ์์ ํตํด, Java app ์ ์ฑ๋ฅ์ ์ต์ ํํ๊ณ memory leak ๋ฑ์ ์น๋ช ์ ์ธ ๋ฌธ์ ๋ฅผ ์ง๋จํ ์ ์์ต๋๋ค.
๋ณธ ํฌ์คํ ์์๋ ๊ฐ๋จํ heapdump ๊ฐ ๋ฌด์์ธ์ง, ์ด๋ป๊ฒ ์์ฑํ๊ณ ์ด์ด๋ณด๋์ง๋ฅผ ํ์ธํด๋ณด์๋๋ฐ์
์๊ฐ์ด ๋ถ์กฑํ ๊ด๊ณ๋กโฆ ๐ฅฒ
heapdump ๋ถ์์ ๊ดํด์๋ ํ์๊ธ์์ ๊ณ์ ์งํํ๋๋ก ํ๊ฒ ์ต๋๋ค.
ํน์ ์๋ชป๋ ๋ถ๋ถ์ ๋ฐ๊ฒฌํ์ ๋ค๋ฉด ์ฝ๋ฉํธ ๋จ๊ฒจ์ฃผ์ธ์! ๐โโ๏ธ
Leave a comment