web-dev-qa-db-ja.com

非ヒープメモリリークJVM

Oracleで実行されているUbuntuサーバーにGlassfishv4.0をセットアップしていますJava仮想マシンとjvmプロセスの常駐メモリサイズ(「top」コマンドで取得)は、jvmに作成するメモリがなくなるまで大きくなります新しいスレッド。

私が持っているもの:

  • 1GbのRAMと1.4GHzプロセッサ(1Core)を搭載したVPSサーバー
  • Ubuntuサーバー12.04
  • Java(TM)SEランタイム環境(ビルド1.7.0_51-b13)
  • Java HotSpot(TM)64ビットサーバーVM(ビルド24.51-b03、混合モード)
  • 私のJava EEwebappを実行しているGlassfishv4.0
  • VMは次のパラメーターで実行されます-XX:MaxPermSize = 200m -XX:PermSize = 100m -XX:Xmx = 512m(必要に応じてすべて追加できます)

問題は何ですか:

RAMの使用量(メモリの使用量)は、jvmがネイティブメモリを割り当てられなくなるまで、1時間あたり10〜100mの使用量に応じて常に増加します。

私が試したこと:

  • とにかくjvmがクラッシュするまで時間を節約するだけの最大ヒープ領域を下げました
  • ヒープにメモリリークを検出しないplumbr( https://portal.plumbr.eu/ )をアタッチしました
  • また、最大パーマサイズを低い値に設定しました。

ヒープスペース+ perm genを測定するので、JVMを安定させたいのですが、「top」コマンドはJavaプロセスメモリが850mbまで増加し、その後それ自体を強制終了します。 。JVMには、permスペースとheapよりも多くのメモリが必要であることはわかっていますが、ヒープ領域とperm genにメモリを多く割り当てすぎていると思いますか?ヘルプやガイドは高く評価されます。

ログ出力: http://pakers.lv/logs/hs_err_pid970.log すべてのJVMフラグ: http://pakers.lv/logs/jvm_flags.txt

更新

他に何を試しましたか(提案と自分の発見に基づいて):

  • ヒープスペースを256mに減らして固定し、システムがまだ安定している間にicreasを実行しましたが、システムで許容できる最大ヒープは512mと128mのパーマネントスペースであることに気付きました。 (-Xmx512m、-Xms512m、-XX:PermSize = 128m、-XX:MaxPermSize = 128m)
  • 縮小Javaスレッドサイズ-Xss256k、218k未満に縮小できませんでした(jvmが起動しません)
  • Jvmが64ビットモードで実行されるように-D64を追加しました
  • -XX:+ AggressiveOpts(パフォーマンス最適化を有効にするため)、-XX:+ UseCompressedOops(ヒープ領域のメモリ使用量を削減するため)、-serverフラグを追加してサーバーモードでjvmを起動
  • ヒープスペースのサイズが非常に限られているため、NewRatioを変更して、テニュア世代を少し大きくしました(ヒープスペースの1/3)-XX:NewRatio = 3
  • OOMエラーを検査できるようにGCの診断オプションを追加しました-XX:+ PrintTenuringDistribution -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps -XX:+ HeapDumpOnOutOfMemoryError -Xloggc:/home/myuser/garbage.log

現在のステータスこれらの変更により、最終的にJavaターゲットであったプロセス)の常駐メモリ(RAM使用量)が制限されました。私の場合、512mのヒープスペース+ 128mのパーマ生成スペースにより、約750mの常駐メモリがJavaプロセスで安定しています。まだメモリの問題がありますが、ヒープメモリがときどきいっぱいになります。ガベージコレクションが継続するため、Webアプリがフリーズしますが、OSはプロセスを強制終了しません。そのため、システムの使用可能なメモリ(RAM)を増やすか、ヒープ使用量を検査して、アプリケーションのフットプリントを減らす必要があります。 Java EEベース(EJBを使用)私はそれを大幅に減らすことができないかもしれません。とにかく提案に感謝し、もしあれば他の提案を自由に共有してください。

3
Oskars Pakers

あなたが共有したものを考えると、いくつかの可能性があります、例えば:

  • リークのあるJNIライブラリ、または、
  • スレッド作成リーク、または
  • リークの多い動的コードプロキシ(perm-genリーク)、

しかし、私が推測できるのは、ログ出力を提供しなかったか、JVMがOutOfMemoryException(OOM)をスローしていたか、または他の障害が発生したかどうかを示したからです。また、使用されているガベージコレクターについても触れませんでしたが、上記のフラグが使用されている唯一のJVMオプションである場合は、CMSコレクターです。

最初のステップは、次のフラグを追加して、ガベージコレクターのアクションを監視可能にすることです。

-XX:+PrintTenuringDistribution
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+HeapDumpOnOutOfMemoryError
-Xloggc:/path/to/garbage.log

それが実際にOOMである場合は、VisualVMまたは同様のツールを使用してヒープダ​​ンプを分析できます。また、VisualVMを使用して、JMX経由でその場でGCアクションを監視しています。を介したJVM内部への可視性は、次のJVMフラグによって有効にできます。

-Dcom.Sun.management.jmxremote
-Dcom.Sun.management.jmxremote.port=4231
-Dcom.Sun.management.jmxremote.ssl=false
-Dcom.Sun.management.jmxremote.authenticate=false

追加リソース:

更新

ログは確かに役立ちます。ありがとうございました。その特定のログは、ヒープが構成された最大値に達する前に、物理メモリが不足したことを示しています。 〜77Mをmallocしようとしましたが、物理的に残っているのは〜63Mだけでした。

ネイティブメモリ割り当て(malloc)が、予約済みメモリをコミットするために77873152バイトを割り当てることができませんでした。

..

/ proc/meminfo:MemTotal:1018724 kB MemFree:63048 kB

これが私がすることです:

  1. ヒープを減らして、マシンに「収まる」ようにします。最小ヒープと最大ヒープを同じ値に設定して、すぐに収まるかどうかを判断できるようにします。収まらない場合は起動しません。

  2. Javaスタックサイズ(-Xss)を減らすことはできますが、これはスレッドを大量に作成しているようには見えないため、節約はMbを超えることはありません。 64ビットLinuxのデフォルトは256kだと思います。減らしすぎると、スタック割り当てでOOM-ingが開始されます。

  3. テストを繰り返します。

  4. 負荷がかかった状態で短時間実行されている場合は、jmap -dump:file=path_to_file <pid>を使用して鑑別診断用のオンデマンドヒープダンプを生成します。

  5. 次の2つのいずれかが発生する必要があります:(a)リークがある場合、最終的には再び失敗しますが、OOMのタイプは異なる必要があります。または、(b)GCがよりハードに動作するようなリークがなく、完了です。以前にそれを試したことを考えると、縮小した最大サイズも適合しない場合を除いて、前者のケースが考えられます。

  6. OOMを実行する場合は、2つのダンプを比較して、jhatまたは他のヒープアナライザーを使用して増加したものを確認します。

幸運を!

3

JVM起動フラグに-D64を追加して、64ビットモードでプロセスを実行してみてください。

pmap $JVMPIDを実行して、仮想メモリがどのように割り当てられているかを確認できます。クラッシュする前に実行してください。

0