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。
ある期間にわたって同じリクエスト(リクエストのセット)で問題を作成できると思います。 MaxMetaspaceSizeを定義しておくのは良いことです。そうしないと、アプリは成長するまでネイティブメモリを使用します。しかし、私は次の手順から始めます。
-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に接続したら、モニターをクリックして、ロードされたクラスの数を確認します。そこで、ヒープとメタスペースを監視できます。しかし、メタスペースを厳密に監視するために他のツールを追加します。
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
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ルート」を見つけますクラスローダーへの参照を保持しているあなた。
同様の問題があり、根本原因は60Kのクラスファイルがメタスペースメモリに読み込まれることでしたが、何もアンロードされませんでした。JVMargの下に追加すると問題が修正されました。
-Dcom.Sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true
https://issues.Apache.org/jira/browse/CXF-2939
お役に立てれば。
また、Tomcatのような一部の自動デプロイの場合、Tomcat\webappsにバックアップを保存しないでください。そうしないと、バックアップをロードしてそれらのリソースと衝突しようとする可能性があります。