日々の研究開発では、システムのパフォーマンスに問題が発生することがよくありますが、その際、システムのパフォーマンスチューニングを行う必要があります。システムチューニングには、アーキテクチャとコードの最適化、jvmチューニング、オペレーティングシステムのチューニング、データベースのチューニング、Tomcatのチューニング、ネットワークのチューニングなど、さまざまな種類があります。アーキテクチャとコードの最適化は最も効率的なチューニング方法ですが、すべてのパフォーマンスの問題を解決できるわけではありません。今日は、共通のトピックであるjvmチューニングを確認します。
この記事は主に以下を含みます
-
Javaメモリモデルのレビュー
-
JVMチューニングが必要なのはいつですか
-
一般的なOOMの例外とケース
-
JVMには監視ツールが付属しています
-
JVMで一般的に使用されるチューニングパラメータ
-
JVMサードパーティ監視ツール
- チューニングケース
Javaメモリモデルのレビュー
まず、次の図に示すように、HotSpotを使用したJVMのメモリモデルを確認しましょう。
HotSpotメモリモデルは3つの部分に分かれています。
クラスローダー
クラスローダーは、Javaによってコンパイルされた.classファイルをロードし、クラス情報を抽出して、特定のデータ構造のメソッド領域に格納するために使用されます。
ランタイムデータ領域
スレッドスタックとローカルメソッドスタックは、スレッド実行時のメソッド呼び出しなどの関連情報を格納するために使用されます。プログラムカウンタは、バイトコード命令のアドレスをメインメモリに記録します。これら3つのモジュールはすべてスレッドプライベートです。
ヒープには、プログラムの実行時に作成されたオブジェクトが格納されます。
jvm仕様のメソッド領域については、java8より前は、HotSpotによるメソッド領域の実装は永続的な世代でした。java7以降、HotSpotは永続的な世代の削除を開始し、シンボル参照はネイティブヒープに移動され、リテラルおよびクラスの静的変数はjavaヒープに移動されました。Java 8のHotSpotは永続世代を完全に廃止し、永続世代をメタスペースに置き換えて、JVM仕様のメソッド領域を実装しました。メタスペースはクラスのメタデータを格納するために使用され、メモリはローカルメモリに割り当てられ、JVMメモリを占有しません。
ヒープメモリの分布は次のとおりです。
G1ガベージコレクターのヒープスペース割り当て戦略は次のとおりです。
ZGCのメモリ割り当ては、後でより動的で柔軟になるように見えました。この記事では、Java 8を例として取り上げ、G1とZGCについては説明しません。
ちなみに、一般的に使用されるガベージコレクションアルゴリズムを確認してください。
a。クリーニングアルゴリズム:メモリの断片化とメモリ割り当て効率の低下を引き起こします
b。圧縮アルゴリズム:高性能オーバーヘッド
c。コピーアルゴリズム:ヒープの使用効率が低い
一般的なガベージコレクター:
新世代:シリアル、パラレルスカベンジ(スループットに注意を払い、CMSでは使用できません)およびパラレルニュー、すべてマークコピーアルゴリズムを使用
Old age:Serial Old(マーカー圧縮アルゴリズム)とParallel Old(マーカー圧縮アルゴリズム)、およびCMS(マーカースイープアルゴリズム、同時実行性のサポート)、java9ではG1に置き換えられました
実行エンジン
HotSpotインタープリターエグゼキューターは、ロードされたバイトコードを1つずつマシンコードに解釈して実行します。繰り返し実行されるホットスポットコードの場合、JITコンパイラーはバイトコードをマシンコードにコンパイルしてから実行します。
ガベージコレクターは、デッドオブジェクトが占有していたヒープメモリスペースを再利用します。
上記のJVMメモリモデルアーキテクチャ図では、3つの紫色の領域がチューニング時に焦点を当てています。ヒープは、オブジェクトデータが格納される領域です。この領域は、JVMの起動時に指定できるガベージコレクターによって管理されます。JVMチューニングは通常、ヒープのサイズを変更し、最も適切なガベージコレクターを選択することを中心に展開されます。JITコンパイラもアプリケーションのパフォーマンスに大きな影響を与えますが、JVMの新しいバージョンを最適化する必要はありません。
JVMチューニングが必要なのはいつですか
外観の観点から、アプリケーションの応答が遅い、サービスを提供できなくなった場合、またはアプリケーションのスループットが小さく、メモリスペースが大きすぎる場合は、アプリケーションを調整する必要があります。これらの外観には、通常、頻繁なガベージコレクション(OOM)が伴います。
JVMチューニングには一般に3つの指標があります
アプリケーションメモリ
主に、jvmに割り当てられたヒープメモリ。これは、jvmの起動時に-Xmsパラメータと-Xmxパラメータで指定されます。これらは、それぞれ、jvmの起動時に割り当てられたメモリと、実行時に割り当て可能な最大メモリです。
スループット
たとえば、1秒あたりに処理されたトランザクションの数、1時間あたりに完了したバッチタスクの数、1時間あたりのデータベースへの成功した要求の数などです。
応答遅延
アプリケーションがリクエストを受信してからレスポンスを返すまでにかかる時間、またはブラウザがリクエストを送信してからページのレンダリングに至るまでの時間。
一般的なOOMの異常と再発方法
OOMは私たちプログラマーが最も見たい例外ですが、私たちの仕事でよく起こります。これは、jvmに新しく作成されたオブジェクトにスペースを割り当てるのに十分なメモリがなく、ガベージコレクターが使用するのに十分なメモリがない場合にトリガーされ、JavaアプリケーションがOOMをトリガーします。もちろん、Linux自体にもOOMキラーメカニズムがあります。カーネルがプロセスが占有するスペースが多すぎることを監視すると、特にメモリが瞬時に増加する場合、メモリが使い果たされるのを防ぐために、OOMがトリガーされて処理する。Javaの一般的なOOMは次のとおりです。
java.lang.OutOfMemoryError:Javaヒープスペース
この例外の理由は、メモリリークとメモリオーバーフローの2つだけです。メモリがオーバーフローした場合は、JVMパラメータ-Xmx構成を調整してヒープスペースを増やす必要があります。メモリリークの場合は、リークしたコードを見つける必要があります。これについては、後で説明する監視ツールを参照してください。
次のコードは典型的なメモリリークコードであり、起動時に-Xmx512mを設定します
public class HeapSize {
public static void main(String[] args){
List<User> list = new ArrayList<>();
User user = new User();
while (true){
list.add(user);
}
}
}
実行後しばらく待ちます:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at boot.oom.HeapSize.main(HeapSize.java:18)
java.lang.OutOfMemoryError:GCオーバーヘッド制限を超えました
この例外の理由は、ガベージコレクターのGC効率が非常に低いためです。JVMはCPU時間の98%以上をGCの実行に費やしますが、再利用されたメモリはヒープスペースサイズの2%未満であり、GCは連続5回を超えました。
public class GcOverrhead {
public static void main(String[] args){
Map map = System.getProperties();
Random r = new Random();
while (true) {
map.put(r.nextInt(), "value");
}
}
}
上記のコードの開始時にパラメーターを追加します。-Xmx45m-XX:+ UseParallelGC -XX:+ PrintGCDetailsが一定期間実行され、次の例外が発生します。注:このパラメーターは作成者のローカル環境のみであり、変更する必要があります。それに応じて。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Hashtable.addEntry(Hashtable.java:435)
at java.util.Hashtable.put(Hashtable.java:476)
at boot.oom.HeapSize.main(HeapSize.java:20)
この例外は、パラメーター-XXを追加することで回避できます。-UseGCOverheadLimitですが、実際には、自分自身に嘘をついているので、問題を見つけて解決する必要があります。
java.lang.OutOfMemoryError:要求された配列サイズがVM制限を超えています
この例外は簡単に理解できます。要求された配列のサイズがJVMの制限を超えています。これには、次の2つの理由があります。
要求された配列が大きすぎるため、jvmスペースが不十分になります
要求された配列はInteger.MAX_INT-1以上です
次の2つのコード:
このコードは、要求された配列サイズがVMの制限を超えていることを直接スローします
int[] arr = new int[Integer.MAX_VALUE - 1];
このコードは最初にJavaヒープスペースをスローし、次に要求された配列サイズがVM制限を超えています
for (int i = 3; i >= 0; i--) {
try {
int[] arr = new int[Integer.MAX_VALUE-i];
System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
} catch (Throwable t) {
t.printStackTrace();
}
}
結果は次のとおりです。
java.lang.OutOfMemoryError: Java heap space
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError: Java heap space
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at boot.oom.ArraySizeExceeds.main(ArraySizeExceeds.java:12)
java.lang.OutOfMemoryError:MetaSpace
メタスペースについては前に説明しました。この例外は不十分なメタスペースです。解決策は、メタスペースサイズを増やし、パラメーターMaxMetaSpaceSizeを構成することです。参照を開始するときにパラメーターを追加します。
-XX:MaxMetaspaceSize = 2m、エラーを直接報告します:
Error occurred during initialization of VM
OutOfMemoryError: Metaspace
java.lang.OutOfMemoryError:理由のためにサイズバイトを要求します。スワップスペースが不足しています
この例外は、オペレーティングシステムの不十分なスワップスペースが原因で発生します。jvmによって割り当てられる最大メモリは、Xmxなどのいくつかのパラメータによって指定されることがわかっています。jvmに必要な合計メモリがホストによって割り当てられる最大物理メモリを超える場合、スワップスペースが使用されます。スワップスペースが不十分な場合、jvmメモリ割り当ては失敗するため、この例外がスローされます。この例外の場所はより複雑であり、ホスト上の他のプロセスが大量のメモリを消費していることが原因である可能性があります。したがって、スワップスペースを増やし、スワップを無効にし、プロセスを分離するという大雑把な方法を使用することは、より適切な解決策ではありません。
java.lang.OutOfMemoryError:ネイティブスレッドを作成できません
この例外は、オペレーティングシステムレベルでもあります。ご存知のとおり、Javaスレッドはオペレーティングシステムレベルです。Javaがスレッドを申請するたびに、オペレーティングシステムを呼び出してローカルスレッドを作成する必要があります。オペレーティングシステムがスレッドの作成に失敗した場合、上記の例外は次のようになります。スローされます。具体的な理由は次のとおりです。
a。メモリスペースが不足しています。jvmが起動すると、パラメータ-Xssは各スレッドが占めるスタックサイズを指定します。メモリが不足している場合、スレッドの作成は失敗します。
b。オペレーティングシステムのulimitの最大ユーザープロセスパラメータ制限。このパラメータは、オペレーティングシステムが作成できるグローバルスレッドの数を示します。
ulimit -a | grepの「maxuserprocesses」コマンドは、次のように表示できます。
ulimit -uは、このパラメーターを変更できます。たとえば、ulimit -u 10000の場合、オペレーティングシステムは10,000スレッドを作成できます。
c。パラメータsys.kernel.threads-maxlimit、コマンドを渡すことができます
以下に示すように、cat / proc / sys / kernel / threads-maxを表示します。
このパラメーターを変更するには、/ etc /sysctl.confファイルにsys.kernel.threads-max = 10000を追加する必要があります。
d。パラメータsys.kernel.pid_max制限。このパラメータは、スレッドが作成されるたびにpidを割り当てる必要があります。pidの値がこの値より大きい場合、作成は失敗します。コマンドの表示:cat / proc / sys / kernel / pid_max
このパラメーターを変更するには、/ etc /sysctl.confファイルにsys.kernel.pid_max = 10000を追加する必要があります。
private static void crateSlowThread(){
try {
System.out.println(Thread.currentThread());
Thread.currentThread().sleep(15000);
} catch (InterruptedException e)
e.printStackTrace();
}
}
public static void test1() {
while (true){
new Thread(() -> crateSlowThread()).start();
}
}
上記のコードを見て、無限ループでスレッドを継続的に作成すると、最後にこのOOMが再現されます。次の図を参照してください。
次のコードは、高い同時実行性をシミュレートします
public static void test() {
for (int i = 0; i < 20; i ++){
System.out.println(Thread.currentThread());
new Thread(() -> crateSlowThread()).start();
}
}
private static void crateSlowThread(){
try {
System.out.println(Thread.currentThread());
Thread.currentThread().sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@RequestMapping("/createNativeThreads1")
public String createNativeThreads1(){
System.out.println("createNativeThreads test1");
CreateNativeThreads.test1();
return "Sucess!";
}
jmeterでのテスト:
JVMには監視ツールが付属しています
JPSは、ターゲット仮想マシン上のすべてのプロセスを一覧表示します
使用例:jps -mlvV
主要参数
-m 打印传递给主类的参数
-l 打印模块名以及包名
-v 打印jvm启动参数,比如-XX:+HeapDumpOnOutOfMemoryError
-V 输出通过标记的文件传递给JVM的参数
jstatは、主に仮想マシンのパフォーマンスデータを監視します
使用例:jstat -gc -h 2 44074 1s 5
基本参数:
-t展示从虚拟机运行到现在的性能数据
-h n 当n大于0是每隔几行展示行头部信息
vmid 展示虚拟机表示
interval 展示性能采样数据的间隔时间
count 展示性能指标的次数
性能参数:
Class 类加载器统计信息
Compiler 即时编译器统计信息
Gc 堆垃圾回收信息
Gccapacity 各代的空间信息
Gccause 同gcutil
Gcnew 新生代统计信息
Gcnewcapacity 展示新生代空间占用情况
Gcold 老年代统计信息
Gcoldcapacity 展示老年代空间占用情况
Gcmetacapacity:meta space 空间大小信息
Gcutil 统计垃圾收集汇总信息
Printcompilation:Displays Java HotSpot VM compilation method statistics.
jmapは、指定されたプロセスのオブジェクト共有メモリまたはヒープメモリ情報を表示します
使用例:
ヒープ内のすべてのライブオブジェクトをエクスポートします
jmap -dump:live、format = b、file = filename.bin
ヒープ内の生き残ったオブジェクトを印刷する
jmap -histo:live 44074
主要参数如下:
-clstats 展示被加载类的信息
-finalizerinfo 展示所有待 finalize 的对象
-histo 展示各个类的实例数目以及占用内存,并按照内存使用量从多至少的顺序排列
-histo:live 展示堆中的存活对象
-dump 导出 Java 虚拟机堆的快照
-dump:live 只保存堆中的存活对象
システムOOMの後、サービスがハングアップした場合、または監視システムがサービスのシャットダウンと再起動を監視している場合、jmapコマンドを使用してヒープスナップショットをエクスポートできないため、仮想を開始するときに次の2つのパラメーターを追加する必要があります。機械:
-XX:+ HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath = \ dump
jinfoJavaプロセスのパラメータを表示または変更します
使用例:
パラメータ設定情報の表示jinfo44074
プロセスパラメータを変更しますjinfo-flag + HeapDumpAfterFullGC 44074
主なパラメータ:
-flag name 打印参数名是name的参数值
-flag [+|-]name 更改bool类型参数值
-flag name=value 增加参数对
-flags 打印传递给jvm的参数对
-sysprops 打印java系统参数对
jstackは、Javaプロセスのスレッドスタック情報とスレッドによって保持されているロックを出力します
例:jstack 44074
出力は次のとおりです。
常用参数:
-F -l参数无响应是强制打印快照信息
-l 打印有关锁的额外信息比如Locked ownable synchronizers
-m Prints a mixed mode stack trace that has both Java and native C/C++ frames.
JVMで一般的に使用されるチューニングパラメータ
堆空间设置:
-Xmx4g 进程占用的最大堆空间大小,超出后会OOM
-Xms2g 初始化堆空间大小
-Xmn1g 年轻代大小,官方推荐配置为整个堆的3/8
-XX:NewRatio=n 年轻代和老年代空间大小比值
-Xss512k 每个线程占用内存大小
-XX:SurvivorRatio=n:年轻代中Eden区与Survivor区的比值。比如n=4,则Eden和Survivor比值为4:2,survivor占年轻代一半
-XX:MetaspaceSize=512m 元空间大小
-XX:MaxMetaspaceSize=512m 这个参数用于限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存
-XX:MinMetaspaceFreeRatio=N
当进行过Metaspace GC之后,会计算当前Metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长Metaspace的大小。在本机该参数的默认值为40,也就是40%。设置该参数可以控制Metaspace的增长的速度,太小的值会导致Metaspace增长的缓慢,Metaspace的使用逐渐趋于饱和,可能会影响之后类的加载。而太大的值会导致Metaspace增长的过快,浪费内存
-XX:MaxMetasaceFreeRatio=N
当进行过Metaspace GC之后, 会计算当前Metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放Metaspace的部分空间。在本机该参数的默认值为70,也就是70%。
-XX:MaxMetaspaceExpansion=N Metaspace增长时的最大幅度
垃圾收集器设置
-XX:+UseSerialGC 设置串行收集器
-XX:+UseParallelGC 设置并行收集器
-XX:+UseParalledlOldGC 设置并行年老代收集器
-XX:+UseConcMarkSweepGC 设置并发收集器
-XX:ParallelGCThreads=n 设置并行收集器收集时使用的线程数
-XX:MaxGCPauseMillis=n 设置并行收集最大暂停时间
-XX:GCTimeRatio=n 设置垃圾回收时间占程序运行时间的百分比,1/(1+n)
-XX:+DisableExplicitGC 禁止外部调用System.gc()
-XX:MaxTenuringThreshold 年轻代复制多少次才会进入老年代
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps 打印每次垃圾回收前,程序未中断的执行时间
-Xloggc:filename 把gc日志存入文件
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间
-XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/dump
JVMサードパーティ監視ツール
日食マット
ダウンロードリンク:https://www.eclipse.org/mat/downloads.php
EclipseマットはJavaアプリケーションを分析するための非常に一般的なツールであり、Eclipseに統合することも、個別にインストールすることもできます。以前のOOM例外ケースを紹介してみましょう
public static void test(){
List<User> list = new ArrayList<>();
User user = new User();
while (true){
list.add(user);
}
}
アプリケーションコマンドを開始します。
java -jar -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ spring-boot-mybatis-1.0-SNAPSHOT.jar
起動後にこのメソッドを呼び出すと、プログラムはOOMをスローし、ヒープダンプファイルjava_pid46242.hprofを生成します。次に、以下に示すように、マットツールを開いてペアダンプファイルをインポートします。
MAT計算オブジェクトがメモリを占有する方法は2つあります。1つ目は、統計オブジェクトにメモリを使用する浅いヒープです。2つ目は、保持ヒープです。これは、オブジェクト自体が占有するメモリと、オブジェクトのみが参照できる他のオブジェクトが占有するメモリを含め、オブジェクトが参照されなくなったときにガベージコレクタが再利用できる合計メモリをカウントします。上記の円グラフは、保持されたヒープに基づいています。
上記のレポートから、メモリリークがあることがわかります。クリックすると、次の図に示すように、メモリリークが見つかります。
Ali診断ツールArthas
ツールアドレス:
https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-basics
https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-advanced
使用するシーン:
クラスが配置されているjarパッケージを見つけます
例外の原因を調べる
コードが実行されない理由を見つける
オンラインデバッグ
グローバル監視システムのステータス
JVMの実行ステータスのリアルタイム監視
IBMヒープアノライザー
このツールは、ヒープメモリリークを検出するためのグラフィカルツールです。インターフェイスは次のとおりです。
公式ウェブサイトアドレス:
https://www.ibm.com/support/pages/ibm-heapanalyzer
このツールは現在IBMによって更新されておらず、公式WebサイトではMATを推奨しています。
チューニングケース
デッドロック診断
次のコードは、古典的なデッドロックのケースです
public static void test() {
Object lockA = new Object();
Object lockB = new Object();
new Thread(() ->{
synchronized (lockA){
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (lockB){
System.out.println("thread 1");
}
}
}).start();
new Thread(() ->{
synchronized (lockB){
synchronized (lockA){
System.out.println("thread 2");
}
}
}).start();
}
main関数の起動後、実行は完了していません。httpでこのメソッドを呼び出すと、結果が返されないことがわかります。コマンドjstack45309> deadlock.txtを入力し、生成されたファイルを表示すると、以下に示すように、スレッドがBOLOCKED状態になっていることがわかります。
ヒープメモリパラメータ設定
Javaアプリケーションの起動時に次の2つのパラメータを追加すると、詳細なガベージコレクション情報がログに出力されます。
-XX:+ PrintGC
-XX:+ PrintGCDetails
以下は完全なGCログです。分析してみましょう。
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(150528K)] [ParOldGen: 243998K->142439K(172032K)] 243998K->142439K(322560K), [Metaspace: 47754K->47754K(1093632K)], 3.6879500 secs] [Times: user=3.91 sys=0.00, real=3.69 secs]
フルGC:フルGCガベージコレクションを意味します。フルを実行しない場合は、マイナーGCを意味します。
割り当ての失敗:このガベージコレクションの理由は、若い世代に新しいオブジェクトに割り当てるのに十分なメモリがないためです。
[PSYoungGen:0K-> 0K(150528K)]:これらの3つの値は、ガベージコレクション前に若い世代が占有したヒープメモリのサイズ、ガベージコレクション後に若い世代が占有したヒープメモリのサイズを表します。若い世代が占めるヒープメモリの合計サイズ。
[ParOldGen:243998K-> 142439K(172032K)]:これらの3つの値は、それぞれ、ガベージコレクション前に旧世代が占有していたヒープメモリのサイズ、ガベージコレクション後に旧世代が占有していたヒープメモリのサイズを表します。古い世代が占有するヒープメモリの合計サイズ
243998K-> 142439K(322560K):これらの2つの値は、それぞれ、ガベージコレクションの前に使用されたヒープメモリの量、ガベージコレクションの後に使用されたヒープメモリの量、およびヒープスペースの合計サイズを表します
[メタスペース:47754K-> 47754K(1093632K)]:これらの3つの値は、ガベージコレクション前のメタスペースのメモリサイズ、ガベージコレクション後のメタスペースのメモリサイズ、およびメタスペースの合計サイズを表します。
3.6879500秒:このGCの期間
[時間:user = 3.91 sys = 0.00、real = 3.69秒]:これらの3回は、GCスレッドによって消費されたCPU時間、GCプロセスシステムの呼び出しと待機に費やされた時間、およびアプリケーションが一時停止された時間を表します。
ヒープメモリサイズの設定
上記の分析は、老年期のガベージコレクション後に占有されたヒープメモリのサイズが142439K = 139Mであることを示しています。
この値に従ってヒープスペースのサイズを指定します。このアプリケーションでは、-Xmsパラメーターと-Xmxパラメーターを同じサイズに設定することをお勧めします。これにより、初期起動期間中のGCの数を減らし、 JVMは、操作中にOSにメモリを要求しません。これらの2つのパラメータは、旧世代のガベージコレクション後に占有されるヒープメモリのサイズの3〜4倍に設定することをお勧めします。この場合は139M (3〜4)です。公式の推奨事項は若い世代を設定することです。ヒープメモリの合計サイズの3/8になるため、若い世代のサイズは-Xmn139M(3〜4)* 3/8
メタスペースサイズの設定
上記の分析は、メタスペースガベージコレクションが占めるメモリサイズが47754K = 47Mであることを示しています。
-XX:MetaspaceSize -XX:MaxMetaspaceSizeこれらの2つの値は、上記の値の1.2〜1.5倍、つまり47M *(1.2〜1.5)に設定することをお勧めします。
上記の最大分析値を使用すると、起動パラメータは次のようになります。
java -jar -Xms556m -Xmx556m -Xmn208m -XX:MetaspaceSize=70m -XX:MaxMetaspaceSize=70m -XX:+PrintGC -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ spring-boot-mybatis-1.0-SNAPSHOT.jar
ガベージコレクション時間
マイナーGCの場合、消費される時間は若い世代のスペースのサイズに比例し、マイナーGCのトリガー頻度は若い世代のスペースのサイズに反比例します。例は次のとおりです。
ログでサンプリングしたところ、マイナーGCは10秒以内に8回トリガーされ、頻度は回/0.8秒で、これら8つのGCの平均時間は0.05秒= 50ミリ秒です。
システムチューニングインデックスが40msの場合、若い世代のサイズを縮小する必要があります。上記の場合、若い世代のサイズは20%、208m * 80%縮小されます。
最後に、JVMチューニングは永続的なトピックであり、私には限られた能力しかありません。批判や訂正を歓迎します。
参考記事:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.html
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html#t1s2
https://plumbr.io/outofmemoryerror
http://openjdk.java.net/jeps/122
記事のソースコードアドレス:
https://github.com/jinjunzhu/spring-boot-mybatis