JVM性能優化--JVM參數配置,使用JMeter簡單測試配合說明參數調優


一、JVM參數配置

1、常見參數配置

  • -XX:+PrintGC 每次觸發GC的時候打印相關日志
  • -XX:+UseSerialGC 串行回收
  • -XX:+PrintGCDetails 更詳細的GC日志
  • -Xms 堆初始值
  • -Xmx 堆最大可用值
  • -Xmn 新生代堆最大可用值
  • -XX:SurvivorRatio 用來設置新生代中eden空間和from/to空間的比例.
  • -XX:NewRatio 配置新生代與老年代占比 1:2
  • 含以-XX:SurvivorRatio=eden/from=den/to

總結:在實際工作中,我們可以直接將初始的堆大小與最大堆大小相等,
這樣的好處是可以減少程序運行時垃圾回收次數,從而提高效率。

-XX:SurvivorRatio 用來設置新生代中eden空間和from/to空間的比例.

2、堆內存大小配置

使用示例: -Xmx20m -Xms5m
說明: 當下Java應用最大可用內存為20M, 初始內存為5M

byte[] b = new byte[4 * 1024 * 1024];
System.out.println("分配了4M空間給數組");
System.out.print("最大內存");
System.out.println(Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
System.out.print("可用內存");
System.out.println(Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
System.out.print("已經使用內存");
System.out.println(Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");

3、設置新生代比例參數

使用示例:-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

說明:堆內存初始化值20m,堆內存最大值20m,新生代最大值可用1m,eden空間和from/to空間的比例為2/1

byte[] b = null;
for (int i = 0; i < 10; i++) {
b = new byte[1 * 1024 * 1024];
}

4、設置新生代與老年代比例參數

使用示例: -Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
-XX:NewRatio=2

說明:堆內存初始化值20m,堆內存最大值20m,新生代最大值可用1m,eden空間和from/to空間的比例為2/1
新生代和老年代的占比為1/2

二、實戰OutOfMemoryError異常

1、Java堆溢出

錯誤原因: java.lang.OutOfMemoryError: Java heap space 堆內存溢出

解決辦法:設置堆內存大小 // -Xms1m -Xmx10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

// -Xms1m -Xmx10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
List<Object> listObject = new ArrayList<>();
for (int i = 0; i < 10; i++) {
System.out.println("i:" + i);
Byte[] bytes = new Byte[1 * 1024 * 1024];
listObject.add(bytes);
}
System.out.println("添加成功...");

2、虛擬機棧溢出

錯誤原因: java.lang.StackOverflowError 棧內存溢出

棧溢出 產生於遞歸調用,循環遍歷是不會的,但是循環方法里面產生遞歸調用, 也會發生棧溢出。

解決辦法:設置線程最大調用深度

-Xss5m 設置最大調用深度

public class JvmDemo04 {
	 private static int count;
	 public static void count(){
		try {
			 count++;
			 count(); 
		} catch (Throwable e) {
			System.out.println("最大深度:"+count);
			e.printStackTrace();
		}
	 }
	 public static void main(String[] args) {
		 count();
	}
}

3、內存溢出與內存泄漏區別

Java內存泄漏就是沒有及時清理內存垃圾,導致系統無法再給你提供內存資源(內存資源耗盡);

而Java內存溢出就是你要求分配的內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。

內存溢出,這個好理解,說明存儲空間不夠大。就像倒水倒多了,從杯子上面溢出了來了一樣。

內存泄漏,原理是,使用過的內存空間沒有被及時釋放,長時間占用內存,最終導致內存空間不足,而出現內存溢出。

三、垃圾收集器

1、串行與並行收集器

串行回收: JDK1.5前的默認算法 缺點是只有一個線程,執行垃圾回收時程序停止的時間比較長

並行回收: 多個線程執行垃圾回收適合於吞吐量的系統,回收時系統會停止運行

2、serial收集器

串行收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線程去回收。新生代、老年代使用串行回收;新生代復制算法、老年代標記-壓縮;垃圾收集的過程中會Stop The World(服務暫停)

一個單線程的收集器,在進行垃圾收集時候,必須暫停其他所有的工作線程直到它收集結束。

特點:CPU利用率最高,停頓時間即用戶等待時間比較長。

適用場景:小型應用

通過JVM參數-XX:+UseSerialGC可以使用串行垃圾回收器。

3、ParNew收集器

ParNew收集器其實就是Serial收集器的多線程版本。新生代並行,老年代串行;新生代復制算法、老年代標記-壓縮

參數控制:-XX:+UseParNewGC ParNew收集器

-XX:ParallelGCThreads 限制線程數量

4、parallel 收集器

Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關注系統的吞吐量。可以通過參數來打開自適應調節策略,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量;也可以通過參數控制GC的時間不大於多少毫秒或者比例;新生代復制算法、老年代標記-壓縮

采用多線程來通過掃描並壓縮堆

特點:停頓時間短,回收效率高,對吞吐量要求高。

適用場景:大型應用,科學計算,大規模數據采集等。

通過JVM參數 XX:+USeParNewGC 打開並發標記掃描垃圾回收器。

5、cms收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。目前很大一部分的Java應用都集中在互聯網站或B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。

從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基於“標記-清除”算法實現的,它的運作過程相對於前面幾種收集器來說要更復雜一些,整個過程分為4個步驟,包括:

  • 初始標記(CMS initial mark)
  • 並發標記(CMS concurrent mark)
  • 重新標記(CMS remark)
  • 並發清除(CMS concurrent sweep)

其中初始標記、重新標記這兩個步驟仍然需要“Stop The World”。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,並發標記階段就是進行GC Roots Tracing的過程,而重新標記階段則是為了修正並發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。

由於整個過程中耗時最長的並發標記和並發清除過程中,收集器線程都可以與用戶線程一起工作,所以總體上來說,CMS收集器的內存回收過程是與用戶線程一起並發地執行。老年代收集器(新生代使用ParNew)

優點:並發收集、低停頓

缺點:產生大量空間碎片、並發階段會降低吞吐量

采用“標記-清除”算法實現,使用多線程的算法去掃描堆,對發現未使用的對象進行回收。

  • (1)初始標記
  • (2)並發標記
  • (3)並發預處理
  • (4)重新標記
  • (5)並發清除
  • (6)並發重置
    特點:響應時間優先,減少垃圾收集停頓時間

適應場景:大型服務器等。

通過JVM參數 -XX:+UseConcMarkSweepGC設置

6、g1收集器

在G1中,堆被划分成 許多個連續的區域(region)。采用G1算法進行回收,吸收了CMS收集器特點。

特點:支持很大的堆,高吞吐量

  • --支持多CPU和垃圾回收線程
  • --在主線程暫停的情況下,使用並行收集
  • --在主線程運行的情況下,使用並發收集

實時目標:可配置在N毫秒內最多只占用M毫秒的時間進行垃圾回收

通過JVM參數 -XX:+UseG1GC 使用G1垃圾回收器

注意: 並發是指一個處理器同時處理多個任務。

並行是指多個處理器或者是多核的處理器同時處理多個不同的任務。

並發是邏輯上的同時發生(simultaneous),而並行是物理上的同時發生。

來個比喻:並發是一個人同時吃三個饅頭,而並行是三個人同時吃三個饅頭。

四、Tomcat配置調優測試

1、Jmeter壓力測試工具

JMeter是一款在國外非常流行和受歡迎的開源性能測試工具,像LoadRunner 一樣,它也提供了一個利用本地Proxy Server(代理服務器)來錄制生成測試腳本的功能,但是這個功能並不好用。所以在本文中介紹一個更為常用的方法——使用Badboy錄制生成 JMeter 腳本。

簡單的介紹一下Badboy。Badboy是一款不錯的Web自動化測試工具,如果你將它用於非商業用途,或者用於商業用途但是安裝Badboy 的機器數量不超過5台,你是不需要為它支付任何費用的。也許是一種推廣策略,Badboy提供了將Web測試腳本直接導出生成JMeter 腳本的功能,並且這個功能非常好用,也非常簡單。你可以跟着下面的試驗步驟來邁出你在開源世界的第一步。

  1. 通過Badboy的官方網站下載Badboy的最新版本;
  2. 安裝Badboy。安裝過程同一般的Windows 應用程序沒有什么區別,安裝完成后你可以在桌面和Windows開始菜單中看到相應的快捷方式——如果找不到,可以找一下Badboy安裝目錄下的Badboy.exe 文件,直接雙擊啟動Badboy;
  3. 啟動Badboy,你可以看到下面的界面。
    file
    在地址欄(圖中紅色方框標注的部分)中輸入你需要錄制的Web應用的URL——這里我們以http://www.yahoo.com 為例,並點擊GO 按鈕開始錄制。
  4. 開始錄制后,你可以直接在Badboy內嵌的瀏覽器(主界面的右側)中對被測應用進行操作,所有的操作都會被記錄在主界面左側的編輯窗口中——在這個試驗中,我們在Yahoo的搜索引擎中輸入 JMeter 進行搜索。不過你將看到,錄制下來的腳本並不是一行行的代碼,而是一個個Web對象——這就有點像LoadRunner的VuGen中的Tree View視圖;
  5. 錄制完成后,點擊工具欄中的“停止錄制”按鈕,完成腳本的錄制;
  6. 選擇“File -> Export to JMeter”菜單,填寫文件名“login_mantis.jmx”,將錄制好腳本導出為JMeter腳本格式。也可以選擇“File -> Save”菜單保存為Badboy腳本;
  7. 啟動JMeter並打開剛剛生成的測試腳本。

2、什么是吞吐量

QPS:Queries Per Second意思是“每秒查詢率”,是一台服務器每秒能夠相應的查詢次數,是對一個特定的查詢服務器在規定時間內所處理流量多少的衡量標准。

3、測試

3.1、測試串行吞吐量

-XX:+PrintGCDetails -Xmx32M -Xms1M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC 回收次數25次 吞吐量4662 

--> 堆的初始值和堆的最大一致
加大初始堆內存大小-Xms1M 修改為32m
GC 回收次數7次 吞吐量5144

3.2、擴大堆的內存

-XX:+PrintGCDetails -Xmx512M -Xms32M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC 回收次數6次 吞吐量5141
結論:垃圾回收次數和設置最大堆內存大小無關,只和初始內存有關系。
初始內存會影響吞吐量。

3.3、調整初始堆

-XX:+PrintGCDetails -Xmx512M –Xms512M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC回收次數0次 吞吐量6561次
結論:堆的初始值和最大堆內存一致,並且初始堆越大就會高。

3.4、並行回收(UseParNewGC)

-XX:+PrintGCDetails -Xmx512M -Xms512M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParNewGC
-XX:PermSize=32M
GC回收0次 吞吐量6800

3.5、CMS收集器

-XX:+PrintGCDetails -Xmx512M -Xms512M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseConcMarkSweepGC 
-XX:PermSize=32M

3.6、G1回收方式

-XX:+PrintGCDetails -Xmx512M -Xms512M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseG1GC
-XX:PermSize=32M

3、調優總結

初始堆值和最大堆內存內存越大,吞吐量就越高。

最好使用並行收集器,因為並行收集器速度比串行吞吐量高,速度快。

設置堆內存新生代的比例和老年代的比例最好為1:2或者1:3。

減少GC對老年代的回收。

個人博客 蝸牛


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM