JBoss 7アプリケーションサーバーで実行されているかなり大きなアプリケーションがあります。以前はParallelGCを使用していましたが、ヒープが大きく(5 GB以上)、通常はほぼいっぱいになっている一部のサーバーで問題が発生し、非常に長いGCの休止が頻繁に発生しました。
最近、アプリケーションのメモリ使用量を改善し、いくつかのケースでは、アプリケーションが実行される一部のサーバーにさらにRAM=を追加しましたが、これらを作成するためにG1への切り替えを開始しました一時停止の頻度が少なくなったり短くなったりしています。状況は改善されているようですが、以前は発生しなかった奇妙な動作が見られます(ParallelGCを使用)。PermGenはすぐにいっぱいになり、最大値に達するとフルGCがトリガーされます。これにより、通常、アプリケーションスレッドで長い一時停止が発生します(場合によっては、1分以上)。
数か月間、512 MBの最大パーマサイズを使用しており、分析中、ParallelGCを使用すると、パーマサイズは通常、約390 MBで増加しなくなります。ただし、G1に切り替えた後、上記の動作が発生し始めました。最大パーマサイズを1 GBに、さらには1.5 GBに増やしてみましたが、それでもフルGCが発生しています(頻度は低いです)。
このリンク で、使用しているプロファイリングツールのスクリーンショットをご覧いただけます(YourKit Java Profiler)。フルGCがトリガーされたときのEdenおよびOld Genには多くの空き領域がありますが、Permサイズは最大です。Permサイズとロードされたクラスの数は、フルGCの後で大幅に減少しますが、それらは再び増加し始め、サイクルが繰り返されます。コードキャッシュは正常です。 38 MBを超えることはありません(この場合は35 MBです)。
以下は、GCログのセグメントです。
2013-11-28T11:15:57.774-0300:64445.415:[フルGC 2126M-> 670M(5120M)、23.6325510秒] [エデン:4096.0K(234.0M)-> 0.0B(256.0M)生存者:22.0M- > 0.0Bヒープ:2126.1M(5120.0M)-> 670.6M(5120.0M)] [時間:user = 10.16 sys = 0.59、real = 23.64秒]
完全なログ ここ を確認できます(サーバーを起動してから、完全なGCの数分後まで)。
ここにいくつかの環境情報があります:
Javaバージョン「1.7.0_45」
Java(TM)SEランタイム環境(ビルド1.7.0_45-b18)
Java HotSpot(TM)64ビットサーバーVM(ビルド24.45-b08、混合モード)
起動オプション:-Xms5g -Xmx5g -Xss256k -XX:PermSize=1500M -XX:MaxPermSize=1500M -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy -Xloggc:gc.log
だからここに私の質問があります:
これはG1で予想される動作ですか?私は誰かが非常によく似た質問をし、G1がPerm Genでインクリメンタルコレクションを実行すべきだと言っている別の投稿をウェブ上で見つけましたが、答えはありませんでした...
起動パラメーターを改善/修正できるものはありますか?サーバーには8 GBのRAMがありますが、ハードウェアが不足しているようではありません。完全なGCがトリガーされるまでアプリケーションのパフォーマンスは良好です。つまり、ユーザーが大きな遅延を経験して不満を感じ始めます。
Perm Genの増加の原因
知らない人のために、PremGenがどのようにいっぱいになるかを考える簡単な方法を次に示します。若い世代は物事が期限切れになるのに十分な時間がないため、古い世代のスペースに移動します。 Perm Genは、YoungおよびOld Genのオブジェクトのクラスを保持します。YoungまたはOld Genのオブジェクトが収集され、クラスが参照されなくなると、Perm Genから「アンロード」されます。 Old GenはGCを取得せず、Perm Genも実行しません。満杯になると、完全停止のGCが必要になります。詳細は 永続的な世代の提示 を参照してください。
CMSへの切り替え
G1を使用していることは承知していますが、並行マークスイープ(CMS)のローポーズコレクター-XX:+UseConcMarkSweepGC
に切り替える場合は、-XX:+CMSClassUnloadingEnabled
を追加して、クラスのアンロードと永続的な世代のコレクションを有効にしてみてください。
隠された問題 '
JBossを使用している場合、RMI/DGCのgcIntervalは1分に設定されています。 RMIサブシステムは、1分に1回フルガベージコレクションを強制します。これにより、若い世代に集められるのではなく、昇進が強制されます。
GCが適切な収集を行うためには、24時間ではなくても少なくとも1時間に変更する必要があります。
-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000
すべてのJVMオプションのリスト
すべてのオプションを表示するには、これをcmd行から実行します。
Java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version
JBossが使用しているものを確認したい場合は、standalone.xml
に以下を追加する必要があります。すべてのJVMオプションとその設定内容のリストが表示されます。注:使用するには、JVM内にある必要があります。外部で実行すると、JBossが実行されているJVMで何が起こっているかがわかりません。
set "Java_OPTS= -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal %Java_OPTS%"
変更されたフラグのみに関心がある場合に使用するショートカットがあります。
-XX:+PrintcommandLineFlags
診断
jmap を使用して、永続的な生成スペースを消費しているクラスを判別します。出力が表示されます
合計
jmap -permstat JBOSS_PID >& permstat.out
これらの設定は私にとってはうまくいきましたが、システムの設定方法とアプリケーションの動作に応じて、それらが適切かどうかが決まります。
-XX:SurvivorRatio=8
–サバイバースペースの比率を1:8に設定して、サバイバースペースを大きくします(比率が小さいほどスペースが大きくなります)。 SurvivorRatioは、1つのサバイバースペースと比較したエデンスペースのサイズです。サバイバースペースが大きいほど、寿命の短いオブジェクトが若い世代で死ぬ時間が長くなります。
-XX:TargetSurvivorRatio=90
–デフォルトの50%ではなく、90%のサバイバースペースを使用できるため、サバイバースペースメモリの使用率が向上します。
-XX:MaxTenuringThreshold=31
–若い世代から古い世代への早期昇進を防ぐため。寿命の短いオブジェクトをより若い期間で死ぬことを許可します(したがって、昇進を避けます)。この設定の結果、コピーする追加のオブジェクトが原因で、マイナーなGC時間が長くなる可能性があります。この値とサバイバースペースのサイズは、サバイバースペース間でのコピーのオーバーヘッドと、長期間存続する予定のオブジェクトのバランスを取るように調整する必要がある場合があります。 CMSのデフォルト設定はSurvivorRatio = 1024およびMaxTenuringThreshold = 0であり、これにより、スカベンジのすべてのサバイバーがプロモートされます。これは、古い世代を収集する単一の同時スレッドに大きな負荷をかける可能性があります。注:-XX:+ UseBiasedLockingと共に使用する場合、この設定は15にする必要があります。
-XX:NewSize=768m
–初期の若い世代のサイズを指定できます
-XX:MaxNewSize=768m
–若い世代の最大サイズを指定できます
以下は、より広範な JVMオプション リストです。
これはG1で予想される動作ですか?
それは驚くべきことではありません。基本的な仮定は、permgenに入れられたものはほとんど決してゴミにならないということです。そのため、permgen GCは「最後の手段」になると期待できます。つまり、フルGCに強制された場合にのみ、JVMが実行することです。 (OK、この議論は証明のどこにもありません...しかし、それは以下と一致しています。)
他のコレクターが同じ行動をしているという多くの証拠を見てきました。例えば.
私は誰かが非常によく似た質問をし、G1がPerm Genでインクリメンタルコレクションを実行すべきだと言っている別の投稿をウェブ上で見つけましたが、答えはありませんでした...
同じ記事を見つけたと思います。しかし、それが可能でなければならないべきだという誰かの意見は、実際には有益ではありません。
起動パラメーターを改善/修正できるものはありますか?
疑わしい。私の理解では、これはpermgen GC戦略に固有のものです。
最初に多くのpermgenを使用しているものを追跡して修正することをお勧めします...またはJava 8に切り替えると、permgenヒープはもうありません:参照 JDK 8でのPermGenの削除
Permgenリークは考えられる1つの説明ですが、他にもあります。例えば.
String.intern()
の乱用DynamicProxy
を使用して、JVMオプションをランダムに試す前に、最初にPermGenが大きくなる根本的な原因を見つけようとしました。
要約すると、何が非常に多くのクラスを生成するかを調べ、それを削減する方法/ GCを調整する方法を考えます。
乾杯、ディモ
私は 上記の答え に同意します。これは、実際にpermgenを満たしているものを実際に見つけ出すべきであり、根本的な原因を見つけたいのは、クラスローダーのリークに関するものだと強く思います。
this thread がJBossフォーラムにあり、そのような診断されたケースのいくつかを通過し、それらがどのように修正されたかを示します。 この回答 と この記事 は、一般的な問題についても説明しています。その記事には、おそらくあなたが実行できる最も簡単なテストについての言及があります:
症状
これは、アプリケーションサーバーを再起動せずにアプリケーションを再デプロイした場合にのみ発生します。 JBoss 4.0.xシリーズは、このようなクラスローダーリークの影響を受けていました。その結果、JVMがPermGenメモリを使い果たしてクラッシュする前に、アプリケーションを2回以上再デプロイできませんでした。
解決
このようなリークを特定するには、アプリケーションをアンデプロイしてから、フルヒープダンプをトリガーします(その前に必ずGCをトリガーしてください)。次に、ダンプ内にアプリケーションオブジェクトが見つかったかどうかを確認します。もしそうなら、彼らのルートへの参照をたどると、あなたのクラスローダーリークの原因を見つけるでしょう。 JBoss 4.0の場合、唯一の解決策は再デプロイのたびに再起動することでした。
これは、再デプロイメントが関連していると思われる場合に最初に試してみるものです。 このブログ投稿 は以前のものであり、同じことを行っていますが、詳細についても議論しています。投稿に基づいて、実際には何も再デプロイしていない可能性がありますが、permgenはそれ自体で一杯になっています。その場合、クラスの検査+ permgenに追加された他のすべての方法が可能です(以前の回答ですでに述べたように)。
それでも洞察が得られない場合は、次のステップとして、 plumbrツール を試してみます。彼らはある種の あなたのためにリークを見つけることの保証 も持っています。