JVM參數調優
一、調優基本概念
在調整性能時,JM有三個組件
- 堆大小調整
- 垃圾收集器調整
- JIT編譯器調整
大多數調優選項都與調整堆大小和選擇的垃圾收集器有關。
同樣,JIT編譯器對性能也有很大影響,但是這個對程序員自身要求非常高。
通常,在調優Java應用程序時,重點是以下兩個主要目標之一:
- 響應性:應用程序或系統對請求的數據進行響應的速度,對於專注於響應性的應用程序,長的暫停時間是不可接受的,重點是在短時間內做出迴應。
- 吞吐量:側重於在特定時間段內最大化應用程序的工作量,對於專注於吞吐量的應用程序,高暫停時間是可接受的。
一般而言,系統瓶頸核心還是在應用代碼,一般情況下無需過多調優,而且JVM本身在不斷優化的過程。
二、常用JVM參數
文章圍繞Java8,下面的一些參數,在JDK9之後就被淘汰了,加紅進行了凸顯。
參數 | 說明 |
---|---|
-XX:+AlwaysPreTouch | JVM啓動時分配內存,非使用時再分配 |
-XX:ErrorFile= filename | 崩潰日誌 |
-XX:+TraceClassLoading | 跟蹤類加載信息 |
-XX:+PrintClassHistogram | 按下Ctr+ Break後,打印類的信息 |
-Xmx -Xms | 最大堆和最小堆 |
-xx:permSize, -xx:metaspaceSize | 永久代/元數據空間 |
-XX:+HeapDumpOnOutOfMemoryError | 0OM時導出堆到文件 |
-XX:+HeapDumpPath | OOM時堆導出的路徑 |
-XX:+OnOutOfMemoryError | JVM啓動時分配內存,非使用時再分配在OOM時,執行一個腳本 |
java-XX:+PrintFlagsFinal -version 打印所有的-XX參數和默認值
通用GC參數
參數 | 說明 |
---|---|
-XX:ParallelGCThread | 並行GC線程數量 |
-XX:ConcGCThreads | 併發GC線程數量 |
-XX:MaxGCPauseMillis | 最大停頓時間,單位毫秒。GC盡力保證回收時間不超過設定值 |
-XX:GCTimeRatio | 0-100的取值範圍,表示垃圾收集時間佔總時間的比,默認99,即最大允許1%時間進行GC |
-XX. SurvivorRatio | 設置Eden區大小和 Survivor區大小的比例, 8表示兩個 Survivor:Eden=2:8,即一個 Survivor佔年輕代的1/10 |
-XX:NewRatio | 新生代和老年代的比,4表示新生代老年代=1:4,即年輕代佔堆的1/5 |
-verbose:gc, -XX;+printGC | 打印GC的簡要信息 |
-XX:+PrintGCDetails |
打印GC詳細信息 |
-XX:+PrintGCTimeStamps |
打印CG發生的時間戳 |
-Xloggc:log/gc.log | 指定 GC log的位置,以文件輸出 |
-XX:ParallelGCThread | 每次一次GC後,都打印堆信息 |
三、GC調優思路
首先準備環境
創建一個springboot的空項目,在啓動類部分添加一下代碼,打包,放在阿里雲服務器上運行。
測試環境:JVM配置爲1核2G,JAVA8,固定設置堆大小 1G
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
// 每333毫秒創建150線程,每個線程創建一個512kb的對象,最多一秒同時存在450線程,佔用內存225m,查看6C的情況
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
new Thread(() -> {
for (int i = 0; i < 150; i++) {
try {
// 不幹活,專門創建512kb的小對象
byte[] temp = new byte[1024 * 512];
Thread.sleep(new Random().nextInt(100)); // 隨機睡眠200毫秒秒以內
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}, 1000, 333, TimeUnit.MILLISECONDS);
}
}
在運行之前要記得指定一下堆的大小
java -Xmx1024m -jar xxx.jar
啓動是正常的
上面已經能看到端口號了,但是我們這裏用jcmd象徵的看一下
我們看一下堆的使用情況
裏面的數據都是動態的。
我們看一下調優的思路
- 分析場景。例如:啓動速度慢;偶爾出現響應慢於平均水平或者出現卡頓
- 確定目標。內存佔用、低延時、吞吐量
- 收集日誌。通過參數配置收集GC日誌;通過JDK工具查看GC狀態(例如jstat 實時查看)
- 分析日誌。使用工具輔助分析日誌,查看GC次數,GC時間(事後分析)
- 調整參數。切換垃圾收集器或者調整垃圾收集器參數
前兩點的話,要和具體場景結合起來。文章主要展示整個過程該怎麼走(相當於工具說明書),具體問題要具體分析。
java -Xmx1024m -Xloggc:/opt/gc.log -jar xxx.jar
我們這裏添加了日誌的收集
我們去對應目錄找一下日誌,裏面的參數我們都介紹過,這個不難看懂。
但是文件內容是非常非常多的,這樣一點點的看可能有點困難,我們一邊把文件下載下來,用GCViewer這個開源的工具來做這件事情。
GCViewer工具,輔助分析GC日誌文件 https://github.com/chewiebug/GCViewer
打開下載到本地的gc.log文件
再看右下角的信息,主要是一些彙總信息。各種數據的分析,比如平均,最大,最小等。
至於首頁的圖形,怎麼看,可以直接參考文檔:https://github.com/chewiebug/GCViewer
紅線意思是什麼等等 都寫的很詳細。
下面看一下jstat 動態監控GC統計信息,採用的格式是間隔1000毫秒統計一次,每10行數據後輸出列標題
jstat -gc -h10 $(jcmd | grep "demo-0.0.1-SNAPSHOT.jar" | awk '{print $1}') 1000
裏面的參數,我們說過了,參考上一篇文章【JVM實戰】基於JDK命令行工具的監控
下面看一下切換垃圾收集器或者調整垃圾收集器參數相關信息:
垃圾收集器 Parallel參數調優
這是JDK默認的收集器–吞吐量優先
參數 | 說明 |
---|---|
-XX:+UseParallelGC | 新生代使用並行回收收集器 |
-XX:+Use ParalleloldGC | 老年代使用並行回收收集器 |
-XX:ParallelGCThreads | 設置用於垃圾回收的線程數 |
-XX:+UseAdaptiveSizePolicy | 打開自適應GC策略 |
自適應GC策略是指自動調整分代分區的大小。UseAdaptiveSizePolicy自適應默認開啓。
垃圾收集器CMS參數調優
關於CMS
- 響應時間優先2
- Parallel gc無法滿足應用程序延遲要求時再考慮使用CMS垃圾收集器。
- 新版建議用G1垃圾收集器
垃圾收集器G1參數調優
關於G1:
- 兼顧吞吐量和響應時間
- 超過50%的Java堆被實時數據佔用。
- 建議大堆(大小約爲6GB或更大)
- 且GC延遲要求有限的應用(穩定且可預測的暫停時間低於0.5秒)。
運行時JIT編譯器優化參數
JIT編譯指的是字節碼編譯爲本地代碼(彙編)執行,只有熱點代碼纔會編譯爲本地代碼。解釋器執行節約內存,反之可以使用編譯執行來提升效率
最後由於版本不斷更新,JVM參數和具體說明,建議需要時參考 oracle官網的手冊。
說實話,如果沒有一個具體的線上場景,很難去演示調優的過程,這裏能做到的就是,給出調優的思路、相關工具的使用,以及調優用到的參數信息。
參數的調整多少都會對JVM的運行有影響,比如 Parallel參數調優中-XX:ParallelGCThreads
設置線程的數量,這個在不同配置的服務器上測試得到的結果會有差別,你可以在自己服務器上試試看。