讓JVM按照預期GC

加入笨神(公眾號:你假笨)帶頭創建的JVMPocket群已經有一段時間了,有幸得到笨神指導一二,學到了不少東西,也糾正了自己對JVM一些誤解,還能給群里一些朋友提出的問題給出指引,榮幸榮幸;JVM博大精深,網上對它的解釋和認知魚龍混雜,如果沒有深厚的功底很難辨別真假;不用擔心,每個行業總有一些大牛是讓我們凡人膜拜的,JVM界公認的大牛就是rednaxelaFX了,圈子人稱R大,且這么評價他:

  • R大說的是對的;
  • R大說的是對的;
  • R大說的是對的;

R大確實牛逼,先show一下他工作履歷:淘寶JVM組,美國OracleJVM組,Azul-ZingVM項目組;R大不僅牛逼,還非常謙虛活躍,在知乎上@他的問題,基本上都能得到他非常非常非常詳盡的解答,對JVM有興趣的朋友,強烈推薦關注他的知乎;另外,一些關注JVM的朋友基本上都看了周志明的<<深入理解Java虛擬機>>,R大對這本書一些有問題的地方也給出了更正,有興趣的朋友可以看看,請點擊傳送門

讓JVM按照預期GC

說起在JVMPocket里事情太多太多了,今天只說一下入群第一天笨神給我們出的一道題目:寫出讓JVM先3次YoungGC再1次CMS GC的代碼;JVM比較復雜,很少有人能深入了解它,對絕大部分程序員來說JVM都是黑盒子;那么對我們這些不是專門從事JVM工作的程序員來說,了解它的第一步就是:知道它大概怎么運行,讓它按照自己的方式運行;所以笨神的這道題目很有價值,下面給出我的答案,源碼如下:

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) {

        // 預期YoungGC的次數
        int ygcTime = 3;

        for (int i=0; i<ygcTime; i++){
            // 由于Eden區設置為8M, 所以分配8個1M就會導致一次YoungGC
            for(int j=0; j<8; j++){
                byte[] tmp = new byte[_1M];
            }
        }

        for(int j=0; j<3; j++) {
            // 對象超過了Eden區, 所以直接在Old區分配; 
            byte[] tmp = new byte[_8M];
        }

        try {
            // sleep一段時間是為了讓CMS GC線程能夠有足夠的時間檢測到Old區達到了觸發CMS GC的條件,CMS GC線程默認2s掃描一次,可以通過參數CMSWaitDuration配置,例如-XX:CMSWaitDuration=3000
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

程序配套的JVM參數:
-verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xmx40m -Xms40m -Xmn10m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly
說明:這樣配置后,Eden區8M,S0/S1區各1M,old區30M,且當Old區占用60%就達到觸發CMS GC的條件;示例代碼中3次分配總計3*8M=24M>30*60%,達到了觸發CMS GC的條件;

一點點瑕疵

這段代碼依然有一點點遺憾,為了達到觸發CMS GC的條件,每次分配的對象不能小于Eden區的大小(在這段代碼中每次分配8M),如果要求Old區的大小在滿足不大于Eden區的大小同樣能達到需求,怎么辦?

更完美的方案

這里利用一個JVM參數PretenureSizeThreshold實現更完美的方案;用法:-XX:PretenureSizeThreshold=2M,含義是:當分配的對象超過設定值時不在Eden區分配,直接在Old區分配;

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);
        // 在這里想怎么觸發GC就怎么調用ygc()和cmsGc()兩個方法
    }

    /**
     * @param n 預期發生n次young gc
     */
    private static void ygc(int n){
        for (int i=0; i<n; i++){
            // 由于Eden區設置為8M, 所以分配8個1M就會導致一次YoungGC
            for(int j=0; j<8; j++){
                byte[] tmp = new byte[_1M];
            }
        }
    }

    /**
     * @param n 預期發生n次CMS gc
     */
    private static void cmsGc(int n){
        for (int i=0; i<n; i++){
            for(int j=0; j<3; j++) {
                // 由于設置了-XX:PretenureSizeThreshold=2M, 所以分配的2M對象不會在Eden區分配而是直接在Old區分配
                byte[] tmp = new byte[_2M];
            }
            try {
                // sleep10秒是為了讓CMS GC線程能夠有足夠的時間檢測到Old區達到了觸發CMS GC的條件并完成CMS GC, CMS GC線程默認2s掃描一次,可以通過參數CMSWaitDuration配置,例如-XX:CMSWaitDuration=3000
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

程序配套的JVM參數:
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xmx20m -Xms20m -Xmn10m -XX:PretenureSizeThreshold=2M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly
說明:這樣配置后,Eden區8M,S0/S1區各1M,old區10M,且當Old區占用60%就達到觸發CMS GC的條件;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文閱讀 前言 這段時間懈怠了,罪過! 最近看到有同事也開始用上了微信公眾號寫博客了,挺好的~給他們點贊,這博客我...
    碼農戲碼閱讀 6,027評論 2 31
  • 作者:一字馬胡 轉載標志 【2017-11-12】 更新日志 日期更新內容備注 2017-11-12新建文章初版 ...
    beneke閱讀 2,236評論 0 7
  • 參數設置 在Java虛擬機的參數中,有3種表示方法用“ps -ef |grep "java"命令,可以得到當前Ja...
    九問閱讀 9,190評論 2 52
  • 轉載blog.csdn.net/ning109314/article/details/10411495/ JVM工...
    forever_smile閱讀 5,402評論 1 56
  • JVM架構 當一個程序啟動之前,它的class會被類裝載器裝入方法區(Permanent區),執行引擎讀取方法區的...
    cocohaifang閱讀 1,700評論 0 7