web-dev-qa-db-ja.com

java8 "Java.lang.OutOfMemoryError:Metaspace"

Javaアプリケーション(Tomcatで実行されているサービス)JREをJava 7からJava 8に切り替えました。 Java.lang.OutOfMemoryError: Metaspace大量のトラフィックで数日間実行した後。

ヒープ使用量は問題ありませんでした。パフォーマンステスト中に同じコードフローが実行された後、メタスペースがジャンプします。

メタスペースメモリの問題の原因は何ですか?

現在の設定は:

-server -Xms8g -Xmx8g -XX:MaxMetaspaceSize=3200m  -XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC -XX:MaxGCPauseMillis=1000 
-XX:+DisableExplicitGC -XX:+PrintGCDetails 
-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=7 -XX:NewSize=5004m 
-XX:MaxNewSize=5004m -XX:MaxTenuringThreshold=12 
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintFlagsFinal  
-XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution 
-XX:+PrintGCCause -XX:+PrintAdaptiveSizePolicy 
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=200M 

また、アプリケーションはリフレクションを多用しています。また、カスタムクラスローダーを使用します。それらはすべてJava 7。

25
Jane

ある期間にわたって同じリクエスト(リクエストのセット)で問題を作成できると思います。 MaxMetaspaceSizeを定義しておくのは良いことです。そうしないと、アプリは成長するまでネイティブメモリを使用します。しかし、私は次の手順から始めます。

  1. JVMにロードされるクラスの数が、サーバーに複数回送信するときに同じ要求に対して増え続けるかどうかを確認します。はいの場合、メタスペースにロードされたクラスの成長を引き起こす動的クラスを作成している可能性があります。ロードされたクラスの数を確認する方法は、visualvmを使用してJMXを使用してサーバーに接続するか、ローカルで実行してシミュレートできます。 localの手順について説明しますが、JMXをリモートでアタッチする場合は、アプリケーションのJVMパラメーターに以下を追加して起動し、ポート9999で-XX:+ UnlockDiagnosticVMOptionsを使用してリモート接続する必要があります。
   -Dcom.Sun.management.jmxremote -Dcom.Sun.management.jmxremote.port=9999 -Dcom.Sun.management.jmxremote.authenticate=false -Dcom.Sun.management.jmxremote.ssl=false -XX:+UnlockDiagnosticVMOptions

Visualvm(jvisualvm)をJVMに接続したら、モニターをクリックして、ロードされたクラスの数を確認します。そこで、ヒープとメタスペースを監視できます。しかし、メタスペースを厳密に監視するために他のツールを追加します。

  1. また、jvmに接続したら、ヒープスナップショットを取得し、OQLを使用してロードされたクラスを見つけることができます。そのため、ヒープダンプを取得する前に、サーバーへのリクエストを停止します。これにより、実行中のリクエスト/実行コードとそれらに関連付けられたオブジェクトをキャッチできませんが、必須ではありません。同じリクエストのセットを複数回実行した後、visualvm内の「モニター」スペースで、右上の「ヒープダンプ」をクリックします。スナップショットを開く/ロードすると、OQLコンソールのオプションが表示されます。 permgen分析の下の右下のパネルにいくつかの定義済みOQLクエリが表示されます「classloader loaded class histogram」という名前のクエリを実行すると、各クラスローダーによってロードされたクラスの数がわかります。クラスをロードしています。

select map(sort(map(heap.objects( 'Java.lang.ClassLoader')、 '{loader:it、count:it.classes.elementCount}')、 'lhs.count <rhs.count')、 'toHtml (it)+ "
"')

ただし、「classloader loaded class」という名前の上記のクエリは遅く、実際には各クラスローダーによってロードされたクラスが表示されます。

select { loader: cl,
             classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
    from instanceof Java.lang.ClassLoader cl
  1. 次に、メタスペース領域の成長を追跡してみてください。ここで、jconsoleと、Java has:jmc(Javaミッションコントロール))を使用します。jconsoleを使用して、jvm(ローカルまたはリモート)に接続し、接続したらメモリに移動します。タブをクリックすると、そこにヒープ以外の増加を監視できます。これには、メタスペースとコードキャッシュ、圧縮されたクラススペースが必要です。

jmc

VMに接続し、接続したら、右上にあるJMCの[診断コマンド]をクリックします。UnlockDiagnosticVMOptionsを有効にしているため、GC.class_statsを実行できます。すべての列を表示してcsvで印刷して実行すると、コマンドは次のようになります。

GC.class_stats -all=true -csv=true

そして、異なる期間のクラス統計を比較し、どのクラスが問題を引き起こしているのか(メタスペースの成長)、またはメタクラス内の関連情報を持っているクラス(メソッド/メソッドデータ)を見つけることができます。一定期間にわたって収集されたcsv出力を分析する方法:csvを取得し、データベースまたは他の場所にある2つの同様のテーブル(csvを表す)にロードして、GC.class_stats csv出力を比較してSQLまたは他の分析ツール。それは、メタスペースで何が正確に成長しているかのより良い考えを与えるでしょう。 GCクラスの統計情報には次の列があります。

インデックス、スーパー、InstSize、InstCount、InstBytes、Mirror、KlassBytes、K_secondary_supers、VTab、ITab、OopMap、IK_methods、IK_method_ordering、IK_default_methods、IK_default_vtable_indices、IK_local_interfaces、_s_notations、IK_transitive_interfaces、IK_transition_interfaces__ methods_parameter_annotations、methods_type_annotations、methods_default_annotations、annotations、Cp、CpTags、CpCache、CpOperands、CpRefMap、CpAll、MethodCount、MethodBytes、ConstMethod、MethodData、StackMap、Bytecodes、MethodAll、ROAll、RWAll、Total、ClassName、ClassLoader

それが役に立てば幸い。また、1.7でリークが発生しない場合、バグはJava 8にある可能性があります。

また、クラスローダーへの参照を保持しているクラスがある場合、クラスはメタスペースからアンロードされません。クラスローダーがGCされるはずであり、クラスローダーへの参照を保持してはならないことがわかっている場合は、visualvmのヒープダンプに戻り、クラスローダーインスタンスをクリックして右クリックし、「最も近いGCルート」を見つけますクラスローダーへの参照を保持しているあなた。

24
dillip pattnaik

同様の問題があり、根本原因は60Kのクラスファイルがメタスペースメモリに読み込まれることでしたが、何もアンロードされませんでした。JVMargの下に追加すると問題が修正されました。

-Dcom.Sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true

https://issues.Apache.org/jira/browse/CXF-2939

お役に立てれば。

5
user1550159

また、Tomcatのような一部の自動デプロイの場合、Tomcat\webappsにバックアップを保存しないでください。そうしないと、バックアップをロードしてそれらのリソースと衝突しようとする可能性があります。

0
Dove