JVM性能優(yōu)化--JVM參數(shù)配置,使用JMeter簡(jiǎn)單測(cè)試配合說(shuō)明參數(shù)調(diào)優(yōu)

一、JVM參數(shù)配置

1、常見參數(shù)配置

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

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

-XX:SurvivorRatio 用來(lái)設(shè)置新生代中eden空間和from/to空間的比例.

2、堆內(nèi)存大小配置

使用示例: -Xmx20m -Xms5m
說(shuō)明: 當(dāng)下Java應(yīng)用最大可用內(nèi)存為20M, 初始內(nèi)存為5M

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

3、設(shè)置新生代比例參數(shù)

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

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

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

4、設(shè)置新生代與老年代比例參數(shù)

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

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

二、實(shí)戰(zhàn)OutOfMemoryError異常

1、Java堆溢出

錯(cuò)誤原因: java.lang.OutOfMemoryError: Java heap space 堆內(nèi)存溢出

解決辦法:設(shè)置堆內(nèi)存大小 // -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、虛擬機(jī)棧溢出

錯(cuò)誤原因: java.lang.StackOverflowError 棧內(nèi)存溢出

棧溢出 產(chǎn)生于遞歸調(diào)用,循環(huán)遍歷是不會(huì)的,但是循環(huán)方法里面產(chǎn)生遞歸調(diào)用, 也會(huì)發(fā)生棧溢出。

解決辦法:設(shè)置線程最大調(diào)用深度

-Xss5m 設(shè)置最大調(diào)用深度

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、內(nèi)存溢出與內(nèi)存泄漏區(qū)別

Java內(nèi)存泄漏就是沒有及時(shí)清理內(nèi)存垃圾,導(dǎo)致系統(tǒng)無(wú)法再給你提供內(nèi)存資源(內(nèi)存資源耗盡);

而Java內(nèi)存溢出就是你要求分配的內(nèi)存超出了系統(tǒng)能給你的,系統(tǒng)不能滿足需求,于是產(chǎn)生溢出。

內(nèi)存溢出,這個(gè)好理解,說(shuō)明存儲(chǔ)空間不夠大。就像倒水倒多了,從杯子上面溢出了來(lái)了一樣。

內(nèi)存泄漏,原理是,使用過(guò)的內(nèi)存空間沒有被及時(shí)釋放,長(zhǎng)時(shí)間占用內(nèi)存,最終導(dǎo)致內(nèi)存空間不足,而出現(xiàn)內(nèi)存溢出。

三、垃圾收集器

1、串行與并行收集器

串行回收: JDK1.5前的默認(rèn)算法 缺點(diǎn)是只有一個(gè)線程,執(zhí)行垃圾回收時(shí)程序停止的時(shí)間比較長(zhǎng)

并行回收: 多個(gè)線程執(zhí)行垃圾回收適合于吞吐量的系統(tǒng),回收時(shí)系統(tǒng)會(huì)停止運(yùn)行

2、serial收集器

串行收集器是最古老,最穩(wěn)定以及效率高的收集器,可能會(huì)產(chǎn)生較長(zhǎng)的停頓,只使用一個(gè)線程去回收。新生代、老年代使用串行回收;新生代復(fù)制算法、老年代標(biāo)記-壓縮;垃圾收集的過(guò)程中會(huì)Stop The World(服務(wù)暫停)

一個(gè)單線程的收集器,在進(jìn)行垃圾收集時(shí)候,必須暫停其他所有的工作線程直到它收集結(jié)束。

特點(diǎn):CPU利用率最高,停頓時(shí)間即用戶等待時(shí)間比較長(zhǎng)。

適用場(chǎng)景:小型應(yīng)用

通過(guò)JVM參數(shù)-XX:+UseSerialGC可以使用串行垃圾回收器。

3、ParNew收集器

ParNew收集器其實(shí)就是Serial收集器的多線程版本。新生代并行,老年代串行;新生代復(fù)制算法、老年代標(biāo)記-壓縮

參數(shù)控制:-XX:+UseParNewGC ParNew收集器

-XX:ParallelGCThreads 限制線程數(shù)量

4、parallel 收集器

Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關(guān)注系統(tǒng)的吞吐量??梢酝ㄟ^(guò)參數(shù)來(lái)打開自適應(yīng)調(diào)節(jié)策略,虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間或最大的吞吐量;也可以通過(guò)參數(shù)控制GC的時(shí)間不大于多少毫秒或者比例;新生代復(fù)制算法、老年代標(biāo)記-壓縮

采用多線程來(lái)通過(guò)掃描并壓縮堆

特點(diǎn):停頓時(shí)間短,回收效率高,對(duì)吞吐量要求高。

適用場(chǎng)景:大型應(yīng)用,科學(xué)計(jì)算,大規(guī)模數(shù)據(jù)采集等。

通過(guò)JVM參數(shù) XX:+USeParNewGC 打開并發(fā)標(biāo)記掃描垃圾回收器。

5、cms收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。目前很大一部分的Java應(yīng)用都集中在互聯(lián)網(wǎng)站或B/S系統(tǒng)的服務(wù)端上,這類應(yīng)用尤其重視服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短,以給用戶帶來(lái)較好的體驗(yàn)。

從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,它的運(yùn)作過(guò)程相對(duì)于前面幾種收集器來(lái)說(shuō)要更復(fù)雜一些,整個(gè)過(guò)程分為4個(gè)步驟,包括:

  • 初始標(biāo)記(CMS initial mark)
  • 并發(fā)標(biāo)記(CMS concurrent mark)
  • 重新標(biāo)記(CMS remark)
  • 并發(fā)清除(CMS concurrent sweep)

其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要“Stop The World”。初始標(biāo)記僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度很快,并發(fā)標(biāo)記階段就是進(jìn)行GC Roots Tracing的過(guò)程,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段稍長(zhǎng)一些,但遠(yuǎn)比并發(fā)標(biāo)記的時(shí)間短。

由于整個(gè)過(guò)程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過(guò)程中,收集器線程都可以與用戶線程一起工作,所以總體上來(lái)說(shuō),CMS收集器的內(nèi)存回收過(guò)程是與用戶線程一起并發(fā)地執(zhí)行。老年代收集器(新生代使用ParNew)

優(yōu)點(diǎn):并發(fā)收集、低停頓

缺點(diǎn):產(chǎn)生大量空間碎片、并發(fā)階段會(huì)降低吞吐量

采用“標(biāo)記-清除”算法實(shí)現(xiàn),使用多線程的算法去掃描堆,對(duì)發(fā)現(xiàn)未使用的對(duì)象進(jìn)行回收。

  • (1)初始標(biāo)記
  • (2)并發(fā)標(biāo)記
  • (3)并發(fā)預(yù)處理
  • (4)重新標(biāo)記
  • (5)并發(fā)清除
  • (6)并發(fā)重置
    特點(diǎn):響應(yīng)時(shí)間優(yōu)先,減少垃圾收集停頓時(shí)間

適應(yīng)場(chǎng)景:大型服務(wù)器等。

通過(guò)JVM參數(shù) -XX:+UseConcMarkSweepGC設(shè)置

6、g1收集器

在G1中,堆被劃分成 許多個(gè)連續(xù)的區(qū)域(region)。采用G1算法進(jìn)行回收,吸收了CMS收集器特點(diǎn)。

特點(diǎn):支持很大的堆,高吞吐量

  • --支持多CPU和垃圾回收線程
  • --在主線程暫停的情況下,使用并行收集
  • --在主線程運(yùn)行的情況下,使用并發(fā)收集

實(shí)時(shí)目標(biāo):可配置在N毫秒內(nèi)最多只占用M毫秒的時(shí)間進(jìn)行垃圾回收

通過(guò)JVM參數(shù) -XX:+UseG1GC 使用G1垃圾回收器

注意: 并發(fā)是指一個(gè)處理器同時(shí)處理多個(gè)任務(wù)。

并行是指多個(gè)處理器或者是多核的處理器同時(shí)處理多個(gè)不同的任務(wù)。

并發(fā)是邏輯上的同時(shí)發(fā)生(simultaneous),而并行是物理上的同時(shí)發(fā)生。

來(lái)個(gè)比喻:并發(fā)是一個(gè)人同時(shí)吃三個(gè)饅頭,而并行是三個(gè)人同時(shí)吃三個(gè)饅頭。

四、Tomcat配置調(diào)優(yōu)測(cè)試

1、Jmeter壓力測(cè)試工具

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

簡(jiǎn)單的介紹一下Badboy。Badboy是一款不錯(cuò)的Web自動(dòng)化測(cè)試工具,如果你將它用于非商業(yè)用途,或者用于商業(yè)用途但是安裝Badboy 的機(jī)器數(shù)量不超過(guò)5臺(tái),你是不需要為它支付任何費(fèi)用的。也許是一種推廣策略,Badboy提供了將Web測(cè)試腳本直接導(dǎo)出生成JMeter 腳本的功能,并且這個(gè)功能非常好用,也非常簡(jiǎn)單。你可以跟著下面的試驗(yàn)步驟來(lái)邁出你在開源世界的第一步。

  1. 通過(guò)Badboy的官方網(wǎng)站下載Badboy的最新版本;
  2. 安裝Badboy。安裝過(guò)程同一般的Windows 應(yīng)用程序沒有什么區(qū)別,安裝完成后你可以在桌面和Windows開始菜單中看到相應(yīng)的快捷方式——如果找不到,可以找一下Badboy安裝目錄下的Badboy.exe 文件,直接雙擊啟動(dòng)Badboy;
  3. 啟動(dòng)Badboy,你可以看到下面的界面。
    file

    在地址欄(圖中紅色方框標(biāo)注的部分)中輸入你需要錄制的Web應(yīng)用的URL——這里我們以http://www.yahoo.com 為例,并點(diǎn)擊GO 按鈕開始錄制。
  4. 開始錄制后,你可以直接在Badboy內(nèi)嵌的瀏覽器(主界面的右側(cè))中對(duì)被測(cè)應(yīng)用進(jìn)行操作,所有的操作都會(huì)被記錄在主界面左側(cè)的編輯窗口中——在這個(gè)試驗(yàn)中,我們?cè)赮ahoo的搜索引擎中輸入 JMeter 進(jìn)行搜索。不過(guò)你將看到,錄制下來(lái)的腳本并不是一行行的代碼,而是一個(gè)個(gè)Web對(duì)象——這就有點(diǎn)像LoadRunner的VuGen中的Tree View視圖;
  5. 錄制完成后,點(diǎn)擊工具欄中的“停止錄制”按鈕,完成腳本的錄制;
  6. 選擇“File -> Export to JMeter”菜單,填寫文件名“l(fā)ogin_mantis.jmx”,將錄制好腳本導(dǎo)出為JMeter腳本格式。也可以選擇“File -> Save”菜單保存為Badboy腳本;
  7. 啟動(dòng)JMeter并打開剛剛生成的測(cè)試腳本。

2、什么是吞吐量

QPS:Queries Per Second意思是“每秒查詢率”,是一臺(tái)服務(wù)器每秒能夠相應(yīng)的查詢次數(shù),是對(duì)一個(gè)特定的查詢服務(wù)器在規(guī)定時(shí)間內(nèi)所處理流量多少的衡量標(biāo)準(zhǔn)。

3、測(cè)試

3.1、測(cè)試串行吞吐量

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

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

3.2、擴(kuò)大堆的內(nèi)存

-XX:+PrintGCDetails -Xmx512M -Xms32M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC 回收次數(shù)6次 吞吐量5141
結(jié)論:垃圾回收次數(shù)和設(shè)置最大堆內(nèi)存大小無(wú)關(guān),只和初始內(nèi)存有關(guān)系。
初始內(nèi)存會(huì)影響吞吐量。

3.3、調(diào)整初始堆

-XX:+PrintGCDetails -Xmx512M –Xms512M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC回收次數(shù)0次 吞吐量6561次
結(jié)論:堆的初始值和最大堆內(nèi)存一致,并且初始堆越大就會(huì)高。

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、調(diào)優(yōu)總結(jié)

初始堆值和最大堆內(nèi)存內(nèi)存越大,吞吐量就越高。

最好使用并行收集器,因?yàn)椴⑿惺占魉俣缺却型掏铝扛撸俣瓤臁?/p>

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

減少GC對(duì)老年代的回收。

個(gè)人博客 蝸牛

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java 虛擬機(jī)有自己完善的硬件架構(gòu), 如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。JVM 屏蔽了與具體操作系...
    尹小凱閱讀 1,747評(píng)論 0 10
  • 垃圾收集基礎(chǔ) Java 語(yǔ)言的一大特點(diǎn)就是可以進(jìn)行自動(dòng)垃圾回收處理,而無(wú)需開發(fā)人員過(guò)于關(guān)注系統(tǒng)資源,例如內(nèi)存資源的...
    Austin_Brant閱讀 816評(píng)論 0 2
  • Java和C++之間有一堵由內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”,墻外面的人想進(jìn)來(lái),墻里面的人想出來(lái)。 對(duì)象...
    胡二囧閱讀 1,324評(píng)論 0 4
  • 內(nèi)存溢出和內(nèi)存泄漏的區(qū)別 內(nèi)存溢出:out of memory,是指程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,...
    Aimerwhy閱讀 799評(píng)論 0 1
  • hbase gc調(diào)優(yōu)(CMS與G1) 一:hbase gc調(diào)優(yōu)目的: 在HBase中,有兩個(gè)在內(nèi)存中的結(jié)構(gòu)消費(fèi)了絕...
    sunTengSt閱讀 4,949評(píng)論 0 5

友情鏈接更多精彩內(nèi)容