一、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)邁出你在開源世界的第一步。
- 通過(guò)Badboy的官方網(wǎng)站下載Badboy的最新版本;
- 安裝Badboy。安裝過(guò)程同一般的Windows 應(yīng)用程序沒有什么區(qū)別,安裝完成后你可以在桌面和Windows開始菜單中看到相應(yīng)的快捷方式——如果找不到,可以找一下Badboy安裝目錄下的Badboy.exe 文件,直接雙擊啟動(dòng)Badboy;
- 啟動(dòng)Badboy,你可以看到下面的界面。
file
在地址欄(圖中紅色方框標(biāo)注的部分)中輸入你需要錄制的Web應(yīng)用的URL——這里我們以http://www.yahoo.com 為例,并點(diǎn)擊GO 按鈕開始錄制。 - 開始錄制后,你可以直接在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視圖;
- 錄制完成后,點(diǎn)擊工具欄中的“停止錄制”按鈕,完成腳本的錄制;
- 選擇“File -> Export to JMeter”菜單,填寫文件名“l(fā)ogin_mantis.jmx”,將錄制好腳本導(dǎo)出為JMeter腳本格式。也可以選擇“File -> Save”菜單保存為Badboy腳本;
- 啟動(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è)人博客 蝸牛
