私は同じ問題に遭遇しました。これは glibc>=2.10 の既知の問題です
解決策は、この環境変数
export MALLOC_ARENA_MAX=4
を設定することです
MALLOC_ARENA_MAX の設定に関する IBM の記事https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
Google で MALLOC_ARENA_MAX を検索するか、SO で検索して多くの参照を見つけてください。
割り当てられたメモリの断片化が少なくなるように最適化するために、他の malloc オプションも調整することをお勧めします。
# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
また、ネイティブ メモリ リークが発生している可能性もあります。一般的な問題は、ZipInputStream
を閉じないことによって引き起こされるネイティブ メモリ リークです。 /GZIPInputStream
.
ZipInputStream
の典型的な方法 Class.getResource
の呼び出しによって開かれます /ClassLoader.getResource
openConnection().getInputStream()
を呼び出す java.net.URL
で インスタンスまたは Class.getResourceAsStream
を呼び出して /ClassLoader.getResourceAsStream
.これらのストリームが常に閉じられるようにする必要があります。
一般的に使用されているオープン ソース ライブラリの一部には、クローズされていない状態でリークするバグがありました java.util.zip.Inflater
または java.util.zip.Deflater
インスタンス。たとえば、Nimbus Jose JWT ライブラリは、6.5.1 バージョンで関連するメモリ リークを修正しました。 Java JWT (jjwt) にも同様のバグがあり、バージョン 0.10.7 で修正されました。この 2 つのケースのバグ パターンは、DeflaterOutputStream.close()
を呼び出すという事実でした。 と InflaterInputStream.close()
Deflater.end()
を呼び出さないでください /Inflater.end()
Deflater
の場合 /Inflater
インスタンスが提供されます。そのような場合、閉じられているストリームのコードをチェックするだけでは十分ではありません。 Deflater
ごと /Inflater
コードで作成されたインスタンスには、その .end()
を処理する必要があります 呼ばれます。
Zip*Stream リークをチェックする 1 つの方法は、ヒープ ダンプを取得し、名前に「zip」、「Inflater」、または「Deflater」を含むクラスのインスタンスを検索することです。これは、Yourkit Java Profiler、JProfiler、Eclipse MAT などの多くのヒープ ダンプ分析ツールで可能です。また、場合によってはファイナライズ後にのみメモリが解放されるため、ファイナライズ状態のオブジェクトをチェックすることも価値があります。ネイティブ ライブラリを使用している可能性のあるクラスをチェックすると便利です。これは TLS/ssl ライブラリにも当てはまります。
java.util.zip.Inflater
のソースを見つけるために使用できる Java エージェントである Elastic の leakchecker と呼ばれる OSS ツールがあります。 閉じられていないインスタンス (.end()
呼び出されません)。
一般的なネイティブ メモリ リーク (zip ライブラリ リークだけでなく) の場合、MALLOC_CONF
の設定を指定して malloc サンプリング プロファイリングを有効にすることで、jemalloc を使用してネイティブ メモリ リークをデバッグできます。 環境変数。詳細な手順については、このブログ投稿 (http://www.evanjones.ca/java-native-leak-bug.html) を参照してください。このブログ投稿には、jemalloc を使用して Java アプリケーションのネイティブ メモリ リークをデバッグする方法に関する情報も含まれています。 jemalloc を紹介し、Elastic がクローズされていない zip インフレータ リソースによって引き起こされる問題を追跡するためにオープンソース化したツール、leakchecker について言及している Elastic からのブログ投稿もあります。
ByteBuffers に関連するネイティブ メモリ リークに関するブログ投稿もあります。 Java 8u102 には、特別なシステム プロパティ jdk.nio.maxCachedBufferSize
があります。 そのブログ投稿で説明されているキャッシュの問題を制限します。
-Djdk.nio.maxCachedBufferSize=262144
また、開いているファイル ハンドルを常にチェックして、大量の mmap:ed ファイルが原因でメモリ リークが発生しているかどうかを確認することもお勧めします。 Linux lsof
の場合 開いているファイルと開いているソケットを一覧表示するために使用できます:
lsof -Pan -p PID
プロセスのメモリ マップのレポートは、ネイティブ メモリ リークの調査にも役立ちます
pmap -x PID
Docker で実行されている Java プロセスの場合、「ホスト」で lsof または pmap コマンドを実行できる必要があります。このコマンドでコンテナ化されたプロセスの PID を見つけることができます
docker inspect --format '{{.State.Pid}}' container_id
また、スレッド ダンプを取得して (または jconsole/JMX を使用して) スレッドの数を確認することも役立ちます。これは、各スレッドがそのスタックに 1 MB のネイティブ メモリを消費するためです。多数のスレッドは大量のメモリを使用します。
JVM にはネイティブ メモリ トラッキング (NMT) もあります。これは、ネイティブ メモリを使い果たしているのが JVM 自体であるかどうかを確認するのに役立つ場合があります。
jattach ツールは、コンテナー化された (docker) 環境でも使用して、ホストからスレッドダンプまたはヒープダンプをトリガーできます。また、NMT の制御に必要な jcmd コマンドを実行することもできます。