Java App でメモリリークらしき事象が発生 Kubernetes で動作する Java アプリケーションのメモリ消費量(working set)を監視していたところ、 起動から数日経過しても安定せず微増し続けた。本環境の Kubernetes は swap 領域がなく、また Pod のメモリサイズも制限していたことから OOM Killed が危ぶまれた。そこで Java プロセスのメモリリー クを想定して原因の調査を開始した。 Memory Usage Time Pod Mem Limit working set Pod のメモリ消費(イメージ) Day 1 Day 2 このままでは OOM Killed の危機 起動後からしばらくは メモリコミットで増加 数日経過しても 安定せず微増し続ける Working set https://ja.wikipedia.org/wiki/%E3%83%AF%E3%83%BC%E3%82%AD%E3%83%B3%E3%82%B0%E3%82%BB%E3%83%83%E3%83%88 Kubernetes - Resource metrics pipeline https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/#memory
System で NMT と working set に差分が生じる事例を調査 JDK-8269345 の Problem で working set の増加と NMT で確認できない問題の記述を発見した。 JDK-8269345: Add Linux-specific jcmd to trim the C-haep https://bugs.openjdk.org/browse/JDK-8269345
は C ヒープの解放されたメモリを OS に返却せずキャッシュとして持ち 続ける特性があり working set が肥大化しやすい Java Process JVM は malloc/mmap でメモリを確保し、不要 になれば free で C ヒープへ返却する。この時、 多くの libc 実装はその一部を OS へ返却するが、 glibc は返却に消極的でキャッシュとして保持す る悪名高い性質がある。特に小さな割当・解放を 頻繁に行うとキャッシュの必要性が高まることで この性質が顕著となり、結果として working set や rss が永続的に増加する。 JVM の NMT は malloc/mmap を追跡するた め、この glibc のキャッシュは参照できない。そ のため NMT の Total と working set/rss に大き な差ができてしまう。 C-heap Operating System JVM NMT retained (cache) 1.free by JVM 2.cache by glibc freed 2.release by other libc allocated working set glibc のキャッシュ保持の様子(イメージ)