web-dev-qa-db-ja.com

大規模アプリケーション向けのJVMパフォーマンスチューニング

デフォルトのJVMパラメータは、大規模なアプリケーションの実行には最適ではありません。実際のアプリケーションでそれを調整した人からの洞察は役に立ちます。クライアントJVMが使用されている32ビットのWindowsマシンでアプリケーションを実行しています デフォルト 。 -serverを追加し、NewRatioを1:3(より大きな若い世代)に変更しました。

あなたが試し、役に立ったと思った他のパラメータ/チューニング?

[更新]私が話している特定のタイプのアプリケーションは、少なくとも-Xmx1024mを必要とする、めったにシャットダウンされないサーバーアプリケーションです。また、アプリケーションはすでにプロファイルされているものとします。 JVMパフォーマンスのみに関する一般的なガイドラインを探しています。

31
amit

そこには大量の情報があります。

最初に、JVMをチューニングする前にコードのプロファイルを作成します。

次に、 JVMドキュメント を注意深く読みます。周りにはたくさんの「都市伝説」があります。たとえば、-serverフラグは、JVMがしばらく常駐して実行されている場合にのみ役立ちます。 -serverはJIT/HotSpotを「ターンアップ」し、ターンアップするには同じパスを何度も通過する必要があります。一方、-serverは、セットアップ時間が長いため、JVMの初期実行がslowsします。

周りにはいくつかの良い本やウェブサイトがあります。たとえば、 http://www.javaperformancetuning.com/ を参照してください。

18
Charlie Martin

序文

バックグラウンド

Javaショップにいる。分散システムでのパフォーマンステストの実行に専念して丸1か月費やした。主なアプリはJavaである。その一部は、Sun自身(当時はOracle)によって開発および販売された製品を意味する。

私が学んだ教訓、JVMに関するいくつかの歴史、内部についてのいくつかの話、説明されているいくつかのパラメーター、そして最後にいくつかのチューニングについて説明します。あなたが実際にそれを適用できるように、それをポイントに保つことを試みること。

Java世界では物事が急速に変化しているので、昨年のすべてのことから、その一部がすでに古くなっている可能性があります。(Is Java 10 out既に?)

良い習慣

あなたがすべきこと:ベンチマーク、ベンチマーク、ベンチマーク!

パフォーマンスについて本当に知る必要がある場合は、ワークロードに固有の実際のベンチマークを実行する必要があります。代替手段はありません。

また、JVMを監視する必要があります。監視を有効にします。優れたアプリケーションは通常、監視WebページやAPIを提供します。それ以外の場合、一般的なJavaツール(JVisualVM、JMX、hprof、およびいくつかのJVMフラグ))があります。

通常、JVMを調整してもパフォーマンスが向上しないことに注意してください。クラッシュするかしないかで「遷移ポイントを見つける」です。これは、アプリケーションにあれの量のリソースを与えると、見返りとしてあれの量のパフォーマンスを一貫して期待できることを知ることです。 知識は力です。

パフォーマンスは、主にアプリケーションによって決定されます。より速くしたい場合は、より良いコードを書く必要があります。

ほとんどの場合、何をしますか:信頼できるデリケートなデフォルトを使用してください

そこにあるすべてのアプリケーションを最適化して調整する時間はありません。ほとんどの場合、適切なデフォルトを使用します。

新しいアプリケーションを構成するときに最初に行うことは、ドキュメントを読むことです。深刻なアプリケーションのほとんどには、JVM設定に関するアドバイスを含む、パフォーマンスチューニングのガイドが付属しています。

次に、アプリケーションを構成できます:Java_OPTS: -server -Xms???g -Xmx???g

  • -server:完全な最適化を有効にします(このフラグは、現在ほとんどのJVMで自動です)
  • -Xms-Xmx:最小ヒープと最大ヒープを設定します(常に両方の値が同じです。これは、実行する唯一の最適化についてのものです)。

これで、JVMについて知っておくべきすべての最適化パラメーターについて知ったと思います。おめでとうございます!それは簡単でした:D

あなたがしてはならないこと

特にそのような複数の行がある場合は、インターネットで見つけたランダムな文字列をコピーしないでください。

-server  -Xms1g -Xmx1g  -XX:PermSize=1g -XX:MaxPermSize=256m  -Xmn256m -Xss64k  -XX:SurvivorRatio=30  -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10  -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark  -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof   -Dcom.Sun.management.jmxremote.port=5616 -Dcom.Sun.management.jmxremote.authenticate=false -Dcom.Sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC

たとえば、グーグルの最初のページにあるこのことは、ひどいものです。引数が複数あり、値が競合しています。一部は、JVMのデフォルトを強制しているだけです(最終的には、2つの前のJVMバージョンのデフォルトです)。いくつかは時代遅れであり、単に無視されます。そして最後に、少なくとも1つのパラメーターが非常に無効であるため、JVMは存在するだけで、起動時にJVMを常にクラッシュさせます。

実際のチューニング

どのようにメモリサイズを選択しますか:

アプリケーションからガイドを読んでください、それはいくつかの指示を与えるはずです。生産を監視し、後で調整します。正確さが必要な場合は、いくつかのベンチマークを実行します。

重要な注意:Javaプロセスは最大max heap PLUS 10までかかります%。X%オーバーヘッドはヒープ管理であり、ヒープ自体には含まれません。

すべてのメモリは通常、起動時にプロセスによって事前に割り当てられます。常に最大ヒープを使用しているプロセスを確認できます。それは単に真実ではありません。 Java監視ツールを使用して、実際に何が使用されているかを確認する必要があります。

適切なサイズを見つける:

  • OutOfMemoryExceptionでクラッシュした場合、メモリが不足しています
  • OutOfMemoryExceptionでクラッシュしない場合は、メモリが多すぎます
  • メモリが多すぎても、ハードウェアがそれを取得したり、すでに支払われている場合、それは完璧な数です。

JVM6はブロンズ、JVM7はゴールド、JVM8はプラチナ...

JVMは永遠に改善されています。ガベージコレクションは非常に複雑なものであり、それに取り組んでいる非常に賢い人がたくさんいます。過去10年間で大幅な改善があり、今後もそうなるでしょう。

情報提供を目的としています。それらは、Oracleで少なくとも4つの利用可能なガベージコレクターですJava 7-8(HotSpot)およびOpenJDK 7-8(他のJVMは完全に異なる場合があります(例:Android、IBM、組み込み)):

  • SerialGC
  • ParallelGC
  • ConcurrentMarkSweepGC
  • G1GC
  • (プラスバリアントと設定)

[Java 7以降。OracleとOpenJDKのコードは部分的に共有されています。GCは(ほとんどの場合)両方のプラットフォームで同じである必要があります。]

JVM> = 7には多くの最適化があり、適切なデフォルトが選択されています。プラットフォームによって少し異なります。それは複数のもののバランスをとります。たとえば、マルチコア最適化を有効にするかどうか、CPUに複数のコアがあるかどうかを決定します。任せてください。 GC設定を変更したり、強制したりしないでください。

コンピューターがあなたに代わって決定を下すのは問題ありません(それがコンピューターの目的です)。すべてのボックスで「常に8コアのアグレッシブコレクションを使用して休止時間を短縮する」よりも、常に95%最適なJVM設定を使用することをお勧めします。その半分は最終的にt2.smallです。

例外:アプリケーションにパフォーマンスガイドと特定のチューニングが用意されている場合。提供された設定をそのままにしても問題ありません。

ヒント:最新の改善点を活用するために新しいJVMに移行すると、それほど労力をかけずに優れたブーストが得られる場合があります。

特殊なケース:-XX:+ UseCompressedOops

JVMには、32ビットインデックスを内部で強制的に使用する特別な設定があります(読み取り:ポインターのような)。これにより、4 294 967 295個のオブジェクト* 8バイトのアドレス=> 32 GBのメモリをアドレス指定できます。 (REALポインターの4GBアドレススペースと混同しないでください)。

全体的なメモリ消費量を削減し、すべてのキャッシュレベルにプラスの影響を与える可能性があります。

実際の例:ElasticSearchのドキュメントでは、実行中の32GB 32ビットノードは、メモリに保持される実際のデータに関して、40GB 64ビットノードと同等である可能性があると述べています。

履歴に関するメモ:このフラグは、Java-7より前の時代(おそらくJava-6より前でも)は不安定であることがわかっていました。それはしばらくの間、新しいJVMで完全に動作しています。

Java HotSpot™仮想マシンのパフォーマンス強化

[...] Java SE 7では、64ビットのJVMプロセスでは、-Xmxが指定されていない場合、および-Xmxの値が32ギガバイト未満の場合、圧縮oopsの使用がデフォルトです。 6u23リリース以前のJDK 6の場合、-XX:+ UseCompressedOopsフラグをJavaコマンドで使用して機能を有効にします。

参照:もう一度、JVMは手動によるチューニングよりも数年先に進んでいます。それでも、それについて知っておくことは興味深いです=)

特殊なケース:-XX:+ UseNUMA

不均一メモリアクセス(NUMA)は、マルチプロセッシングで使用されるコンピュータメモリ設計であり、メモリアクセス時間は、プロセッサに対するメモリの場所に依存します。出典: Wikipedia

最近のシステムは、コアとCPU全体でプライベートと共有の両方のメモリとキャッシュの複数のレイヤーを持つ非常に複雑なメモリアーキテクチャを備えています。

現在のプロセッサのL2キャッシュにあるデータにアクセスすることは、他のソケットからメモリスティックにアクセスするよりもはるかに高速です。

今日販売されているすべてのマルチソケットシステムは設計上NUMAですが、すべてのコンシューマシステムはそうではありません。 Linuxでnumactl --showコマンドを使用して、サーバーがNUMAをサポートしているかどうかを確認します。

NUMA対応フラグは、基盤となるハードウェアトポロジのメモリ割り当てを最適化するようJVMに指示します。

パフォーマンスが大幅に向上する可能性があります(2桁:+ XX%)。実際、「NOT-NUMA 10CPU 100GB」から「NUMA 40CPU 400GB」に切り替える人は、フラグについて知らないとパフォーマンスが[劇的に]失われる可能性があります。

:NUMAを検出し、JVMでフラグを自動的に設定するための議論があります http://openjdk.Java.net/jeps/ 16

ボーナス:ビッグファットハードウェア(NUMAなど)で実行するすべてのアプリケーションを最適化する必要があります。 Javaアプリケーションに固有ではありません。

未来に向けて:-XX:+ UseG1GC

ガベージコレクションの最新の改善点は G1コレクター(読み取り:ガベージファースト) です。

ハイコア、ハイメモリシステムを対象としています。絶対に最低4コア+ 6 GBメモリ。それはそれの10倍以上を使用するデータベースとメモリ集約型アプリケーションを対象としています。

短いバージョンでは、これらのサイズでは、従来のGCは一度に処理するには多すぎるデータに直面しており、一時停止は手に負えなくなっています。 G1は、ヒープを多くの小さなセクションに分割し、アプリケーションの実行中に独立して並行して管理できます。

最初のバージョンは2013年に利用可能になりました。現在は本番環境として十分成熟していますが、すぐにはデフォルトとして機能しなくなります。これは、大規模なアプリケーションで試してみる価値があります。

触れないでください:世代サイズ(NewGen、PermGen ...)

GCはメモリを複数のセクションに分割しました。 (詳細については触れませんが、「Java GC Generations」をググってください。)

最後に1週間を費やして、1万ヒット/秒のアプリで世代フラグの20通りの組み合わせを試してみました。私は-1%から+ 1%の範囲で素晴らしいブーストを得ていました。

Java GCの生成は、論文を読んだり、書いたりする興味深いトピックです。本当に最適化を必要とする1%の人々の中で、わずかな利益のためにかなりの時間を費やすことができる1%のメンバーでなければ、これらを調整する必要はありません。

結論

これがあなたに役立つことを願っています。 JVMを楽しんでください。

Javaは世界で最高の言語と最高のプラットフォームです。愛を広めていく:D

30
user5994461

ここを見てください(またはホットスポットのチューニングをGoogle検索します) http://Java.Sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

VMを調整する前に、必ずアプリのプロファイルを作成してください。 NetBeansには、さまざまなものを表示できるNiceプロファイラが組み込まれています。

私はかつて誰かにアプリのGCが壊れていると言われた-コードを調べたところ、データベースクエリの結果を決して閉じていないため、大量のバイト配列を保持していることがわかりました。結果を閉じると、時間が20分以上の1 GBのメモリから約2分で、非常に少量のメモリになりました。彼らは、JVMチューニングパラメータを削除することができ、満足しました。

7
TofuBeer

32ビットWindowsマシンのJavaでは、選択肢が限られています。私の経験では、以下のパラメーター設定はアプリケーションのパフォーマンスに影響します。

  1. メモリサイズ
  2. gCコレクターの選択
  3. gCコレクターに関連するパラメーター
1
stones333

CPUサンプリングとオブジェクト割り当てモニタリングを同時にオンにして、アプリケーションのプロファイルを作成することをお勧めします。コードの調整に役立つ非常に異なる結果が得られることがわかります。また、組み込みのhprofプロファイラーを使用してみてください。非常に異なる結果になる可能性もあります。

一般に、アプリケーションのプロファイリングは、JVM引数よりもはるかに違いがあります。

1
Peter Lawrey

これに答えるための絶対最良の方法は、作成できる「実稼働」環境にできるだけ近い場所でアプリケーションの制御されたテストを実行することです。 -serverの使用、妥当な開始ヒープサイズ、および最近のJVMの比較的スマートな動作は、通常試行する設定の大部分と同じかそれ以上に機能する可能性があります。

この広範な一般化には特定の例外が1つあります。Webコンテナーで実行している場合は、永続的な生成設定を増やす必要がある可能性が非常に高くなります。

1
mmDonuts

これは、アプリケーション、JVMのベンダーおよびバージョンに大きく依存します。パフォーマンスの問題と見なすものについて明確にする必要があります。コードの特定の重要なセクションに関心がありますか?アプリのプロファイルをもう作成しましたか? JVMはガベージコレクションに時間をかけすぎていますか?

私はおそらく-verbose:gc JVMオプションから始めて、ガベージコレクションがどのように機能しているかを監視します。多くの場合、-Xmxで最大ヒープサイズを増やすだけの最も簡単な修正です。 -verbose:gcの出力を解釈する方法を学ぶと、JVM全体のチューニングについて知っておく必要があるほとんどすべてのことがわかります。しかし、これを単独で実行しても、魔法のようにうまく調整されていないコードが速くなるだけではありません。ほとんどのJVMチューニングオプションは、ガベージコレクターやメモリサイズのパフォーマンスを向上させるように設計されています。

プロファイリングについては yourkit.com が好きです

0
Gary