SLASH 22 - Java Native Memory Leak 리뷰

잡부·2022년 7월 10일
1

개요

최근 자바 AP의 Native Memory Leak을 개선 하다 해당 영상을 보게 됐고, 간단 리뷰를 해본다.


영상의 시작은 OOM Killed 메시지로 시작한다. 해당 내용을 보면 쿠버네티스의 특정 POD의 어플리케이션에서 OOM이 발생해 Slack incoming-webhook을 통해서 노티를 해주는것을 알 수 있다. 모니터링은 Grafana를 통해 모니터링하고 이벤트 발생시 webhook으로 노티하는것을 알 수 있는데, Grafana 대시보드 이미지 까지 노티 해준 다는것이 인상적 이였다. 최근 사내에서 mattermost의incoming-webhook을 통해 필요한 Alert를 통합할 기획을 하고 있는데, 특정 메트릭의 대시보드까지 노티로 보여주면 장애 대응에 효율적이라고 생각했고 활용해 보자는 생각을 가지게 됐다.

또한, OOM이라는 메시지를 Java중심으로 생각 했었는데, 해당 동영상에서는 시스템 로그에 남는 OOM Killed를 의미하는듯 했다. demesg등 명령어로 확인할 수 있고, 리눅스에서 기본적으로 제공한다.

dmesg
[2156747.865271] run invoked oom-killer: gfp mask=0x24201ca, order=0, oom score
adj=0
[...][2156747.865330] Mem-Info:
[2156747.865333] active
anon:3773117 inactive anon:20590 isolated anon:0
[2156747.865333] active file:3 inactive file:0 isolated file:0
[2156747.865333] unevictable:0 dirty:0 writeback:0 unstable:0
[2156747.865333] slab
reclaimable:3980 slab unreclaimable:5811
[2156747.865333] mapped:36 shmem:20596 pagetables:10620 bounce:0
[2156747.865333] free:18748 free
pcp:455 free cma:0
[...][2156747.865385] [ pid ] uid tgid total
vm rss nr ptes nr pmds swapents
oom score adj name
[2156747.865390][ 510] 0 510 4870 67 15 3 0
0 upstart-udev-br
[2156747.865392][ 524] 0 524 12944 237 28 3 0
-1000 systemd-udevd
[...][2156747.865574] Out of memory: Kill process 23409 (perl) score 329 or sacrifice
child
[2156747.865583] Killed process 23409 (perl) total-vm:5370580kB, anon-
rss:5224980kB, file-rss:4kB

영상에서는 NativeMemoryTracking 옵션을 통해 Native 메모리를 트레킹 하는것을 설명하고 있다. 해당 옵션을 사용해보려고 했는데, 내가 운영하고 있는 서버는 자바6 해당 옵션을 사용할 수 없었다. 해당 옵션은 자바7 부터 사용할 수 있는 옵션인듯 하다. 오라클에서 Native Memory Tracking에 대해서 자세하게 설명하고 있다.링크

JNI나 JNA을 사용할 경우 C구현체로 구현된 AP의 경우 C에서 Native Leak이 생길 경우 JVM에서는 이를 인지할 수 없어서 Native memory Leak이 생길 이슈가 있다. PKI등을 사용하는 AP일 경우는 JNI를 쓰는경우가 경험 적으로 많았다. 해당 포인트도 네이티브 릭일때 체크해야할 인사이트를 공유해주고 있다.

JDK 1.4부터 지원하는 DirectBuffer도 네이티브 릭일 경우 하나의 포인트로 잡을 수 있다는것을 배웠다. DirectBuffer 관련 글은 뒷태지존님 글이 잘 나와 있어 첨부 한다. 링크 아무튼 요점은 DirectBuffer도 네이티브 영역에 할당 된다는 점이다. DirectBuffer가 네이티브 메모리 릭에 대한 원인 인지도 NMT옵션을 키고 JVM을 기동하고, 동일하게 jmd 옵션을 통해 DirectBuffer가 할당된 크기를 확인할 수 있다는 점이다.


APM(jennifer, scouter)등을 의미하고 실제로 javaAgent기반으로 동작한다. instrumnet를 변경하는 형태로 동작하는지는 생각해보지 못했던것인데 사실 여기서도 하나를 더 배웠다. Jennifer APM을 운영하면서 BCI(Byte Code Instrumentation)을 정말 많이 봤던 단어 인데 항상 지나쳐 지나갔다.

효율적인 모니터링을 하려면 모니터링을 할 자바 애플리케이션의 소스 코드를 수정하지 않고, 성능 데이터 수집에 필요한 추적 코드와 프로파일 정보 추출 코드를 자바 애플리케이션을 구성하는 클래스에 삽입할 수 있어야 한다. 이를 Byte Code Intrumentation(이하 BCI)이라 한다.


해당 방법도 좋은 방법이다. 경험적으로 나는 해당방법을 사용했을 때 힌트를 찾기는 어려웠다.

pmap -x pid | sort -k 3 -n -r | more

pmap을 RSS 기준으로 정렬할 수 있는 명령어 이다. Total 값에서 Heap 메모리를 제외 하면 Native 영역을 알 수 있다.


사실 이 영상에서 또 하나의 인사이트를 배운부분은 이부분이다. jemalloc, tcmalloc등을 활용할 수있어 보인다. 예상 되는 동작방식은 malloc후 free를 하지 않는 부분을 프로파일링해 주는 방식일 듯 하다.

jemalloc을 활용해 프로파일링 뒤 jeprof를 통해 다이어 그램으로 볼 수 있다. tcmalloc도 비슷하게 제공하는듯 하다.


jeprof를 통해 만든 다이어그램에서 알 수 있는 것은 C2Compiler에서 프로세스 메모리의 90%이상을 사용하고 있다는 것이다. 해당 화면을 보면서 pstack을 여러번 찍었을 때 C2Compier method가 많이 차지하고 있을 지 궁금하기도 했다. Java AP에서 문제가 있을 경우 jstack으로 찍어서 문제 지점을 찾듯이 Native 레벨에서 이슈를 트레킹 하기 위해서는 pstack을 찍어서 확인해 보아야 한다는게 내 생각이다.




java에서 JIT Compiler는 Level0에서 Level4까지 총 5단계로 나눠진다. 이중 5단계에 해당하는 것이 Level 4 컴파일러가 C2 이다.

C1 Compiler는 JIT Compiler의 최적화를 줄이되 빠르게 컴파일되는 컴파일러이며, 이런 특성은 클라이언트 레벨에서 많이 쓰인다. 클라이언트에서는 앱을 띄우면 빠르게 뜨는 것이 중요하니, 많은 최적화보다는 빠르게 컴파일되어 구동시간을 줄이는 것이 중요하다. C2 Compiler는 구동 시간은 많이 느리지만 JIT Compier의 최적화를 많이 하여, 연산할 때는 빠른 연산을 할 수 있는 컴파일러 이다. 이런 특성은 서버에서 적합 하다. 그래서 보통 서버 AP는 C2 Compiler를 사용한다. 사실 이 화면을 보면서 나는 JAVA의 cilent 및 server 옵션이 생각 났다. 비슷하게 동작하는지는 잘 모르겠다.

Java에서는 Tiered Stop at Level이라는 옵션을 주면 JIT Compiler 레벨을 선택할 수 있다.

C1 컴파일러를 사용했을 경우 Native Leak은 제거 됐고, CPU는 40%에서 70%까지 올라갔다고 한다. 최적화가 상대적으로 약하기 때문이라고 추측 된다.


실험적인 옵션을 사용하면 Graal Compiler를 사용할 수 있다.


Canary 배포와 Blue/Green 배포 시스템이 구축되어 있어 문제가 발생하더라도 영향도를 최소화 할 수 있고, 빠른 롤백이 가능하다고 했다.


마지막으로 트러블슈팅에서 중요한건 정답 보다 문제 원인의 실마리를 찾아가는 과정이 더 중요하다고 말씀 하고 있다. 나도 크게 공감하는 부분이고, 실마리를 찾아가기 위해서는 평소에 지속적인 공부, 분석도구에 이해화 활용이 중요하다.

트러블슈팅은 지식기반의로 시작 된다. 하지만 이런 지식기반에서 벗어 나면 트러블 슈팅이 정말 힘들어 진다. 그래서 평소에 지식기반에서 벗어낫을 때 인사이트를 줄 수 있는 observability 시스템을 잘 구축해야 겠다는 생각을 가지고 있다.

영상 시청시 같이 봤던 링크들
1. 메모리 모니터링과 원인 분석
2. Java Process 메모리 추적기
3. native-memory-the-silent-jvm-killer

profile
잡부

0개의 댓글