加入笨神(公眾號:你假笨
)帶頭創(chuàng)建的JVMPocket
群已經(jīng)有一段時間了,有幸得到笨神指導(dǎo)一二塘揣,學(xué)到了不少東西辛掠,也糾正了自己對JVM一些誤解,還能給群里一些朋友提出的問題給出指引进陡,榮幸榮幸愿阐;JVM博大精深,網(wǎng)上對它的解釋和認(rèn)知魚龍混雜趾疚,如果沒有深厚的功底很難辨別真假缨历;不用擔(dān)心,每個行業(yè)總有一些大牛是讓我們凡人膜拜的糙麦,JVM界公認(rèn)的大牛就是rednaxelaFX
了辛孵,圈子人稱R大
,且這么評價他:
- R大說的是對的赡磅;
- R大說的是對的魄缚;
- R大說的是對的;
R大確實牛逼焚廊,先show一下他工作履歷:淘寶JVM組冶匹,美國OracleJVM組,Azul-ZingVM項目組咆瘟;R大不僅牛逼嚼隘,還非常謙虛活躍,在知乎上@他的問題袒餐,基本上都能得到他非常非常非常詳盡的解答嗓蘑,對JVM有興趣的朋友,強(qiáng)烈推薦關(guān)注他的知乎匿乃;另外桩皿,一些關(guān)注JVM的朋友基本上都看了周志明的<<深入理解Java虛擬機(jī)>>,R大對這本書一些有問題的地方也給出了更正幢炸,有興趣的朋友可以看看泄隔,請點(diǎn)擊傳送門;
讓JVM按照預(yù)期GC
說起在JVMPocket里事情太多太多了宛徊,今天只說一下入群第一天笨神給我們出的一道題目:寫出讓JVM先3次YoungGC再1次CMS GC
的代碼佛嬉;JVM比較復(fù)雜逻澳,很少有人能深入了解它,對絕大部分程序員來說JVM都是黑盒子暖呕;那么對我們這些不是專門從事JVM工作的程序員來說斜做,了解它的第一步就是:知道它大概怎么運(yùn)行,讓它按照自己的方式運(yùn)行湾揽;所以笨神的這道題目很有價值瓤逼,下面給出我的答案,源碼如下:
public class CmsGcTest {
private static final int _1M = 1*1024*1024;
private static final int _8M = 8*1024*1024;
public static void main(String[] args) {
// 預(yù)期YoungGC的次數(shù)
int ygcTime = 3;
for (int i=0; i<ygcTime; i++){
// 由于Eden區(qū)設(shè)置為8M, 所以分配8個1M就會導(dǎo)致一次YoungGC
for(int j=0; j<8; j++){
byte[] tmp = new byte[_1M];
}
}
for(int j=0; j<3; j++) {
// 對象超過了Eden區(qū), 所以直接在Old區(qū)分配;
byte[] tmp = new byte[_8M];
}
try {
// sleep一段時間是為了讓CMS GC線程能夠有足夠的時間檢測到Old區(qū)達(dá)到了觸發(fā)CMS GC的條件库物,CMS GC線程默認(rèn)2s掃描一次霸旗,可以通過參數(shù)CMSWaitDuration配置,例如-XX:CMSWaitDuration=3000
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
程序配套的JVM參數(shù):
-verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xmx40m -Xms40m -Xmn10m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly
說明:這樣配置后戚揭,Eden區(qū)8M诱告,S0/S1區(qū)各1M,old區(qū)30M民晒,且當(dāng)Old區(qū)占用60%就達(dá)到觸發(fā)CMS GC的條件精居;示例代碼中3次分配總計3*8M=24M>30*60%,達(dá)到了觸發(fā)CMS GC的條件潜必;
一點(diǎn)點(diǎn)瑕疵
這段代碼依然有一點(diǎn)點(diǎn)遺憾靴姿,為了達(dá)到觸發(fā)CMS GC的條件,每次分配的對象不能小于Eden區(qū)的大泄伪恪(在這段代碼中每次分配8M)空猜,如果要求Old區(qū)的大小在滿足不大于Eden區(qū)的大小同樣能達(dá)到需求,怎么辦恨旱?
更完美的方案
這里利用一個JVM參數(shù)PretenureSizeThreshold實現(xiàn)更完美的方案辈毯;用法:-XX:PretenureSizeThreshold=2M,含義是:當(dāng)分配的對象超過設(shè)定值時不在Eden區(qū)分配搜贤,直接在Old區(qū)分配谆沃;
public class CmsGcTest {
private static final int _1M = 1*1024*1024;
private static final int _2M = 2*1024*1024;
public static void main(String[] args) {
ygc(3);
cmsGc(1);
// 在這里想怎么觸發(fā)GC就怎么調(diào)用ygc()和cmsGc()兩個方法
}
/**
* @param n 預(yù)期發(fā)生n次young gc
*/
private static void ygc(int n){
for (int i=0; i<n; i++){
// 由于Eden區(qū)設(shè)置為8M, 所以分配8個1M就會導(dǎo)致一次YoungGC
for(int j=0; j<8; j++){
byte[] tmp = new byte[_1M];
}
}
}
/**
* @param n 預(yù)期發(fā)生n次CMS gc
*/
private static void cmsGc(int n){
for (int i=0; i<n; i++){
for(int j=0; j<3; j++) {
// 由于設(shè)置了-XX:PretenureSizeThreshold=2M, 所以分配的2M對象不會在Eden區(qū)分配而是直接在Old區(qū)分配
byte[] tmp = new byte[_2M];
}
try {
// sleep10秒是為了讓CMS GC線程能夠有足夠的時間檢測到Old區(qū)達(dá)到了觸發(fā)CMS GC的條件并完成CMS GC, CMS GC線程默認(rèn)2s掃描一次,可以通過參數(shù)CMSWaitDuration配置仪芒,例如-XX:CMSWaitDuration=3000
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
程序配套的JVM參數(shù):
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xmx20m -Xms20m -Xmn10m -XX:PretenureSizeThreshold=2M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly
說明:這樣配置后唁影,Eden區(qū)8M,S0/S1區(qū)各1M掂名,old區(qū)10M据沈,且當(dāng)Old區(qū)占用60%就達(dá)到觸發(fā)CMS GC的條件;