jvm調優思路及調優案例( 二 )


我們回想一下對象進入老年代的幾種方式:

  1. 大對象(代碼排除沒有大對象)
  2. 對象到達一定年齡閾值(通過Young GC觀察沒有達到15次)
  3. 動態對象年齡判斷(Young GC后的存活對象小于Survivor區域的50%)
所以應該是動態對象年齡判斷機制導致Full GC次數變多了 。我們可以嘗試著優化下JVM參數,把年輕代適當調大點 。
-Xms1536M -Xmx1536M -Xmn1024M -Xss256K -XX:SurvivorRatio=6  -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M-XX:+UseParNewGC  -XX:+UseConcMarkSweepGC  -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly可以通過jinfo查看JVM參數是否生效,優化后的內存模型為:
jvm調優思路及調優案例

文章插圖
優化后我們再重新跑一下程序,新的gc變化:
優化完發現沒什么變化,反而是Full GC次數還變多了 。
jvm調優思路及調優案例

文章插圖
我們思考下full gc 比minor gc還多的原因有哪些?
1、元空間不夠導致的多余full gc
2、顯示調用System.gc()造成多余的full gc , 這種一般線上盡量通過-XX:+DisableExplicitGC參數禁用,如果加上了這個JVM啟動參數,那么代碼中調用System.gc()沒有任何效果
3、老年代空間分配擔保機制
可以簡單排除掉前2個原因,第三個老年代空間擔保機制也可以通過觀察minor gc 與full gc的次數比例進行排除,那接下來就可能真的就是程序產生了很多占內存的對象 。我們可以通過jmap、jvisualvm來跟蹤到占內存的對象 。
jmap -histo 27808
jvm調優思路及調優案例

文章插圖
查到了有大量User對象生成,這個可能是問題所在,但不確定 , 還必須找到對應的代碼確認,如何找到對應的代碼有如下幾種方式:
1、代碼里全文搜索生成User對象的地方(適合只有少數幾處地方的情況)
2、如果生成User對象的地方太多,無法定位具體代碼 , 我們可以同時分析下占用cpu較高的線程,一般有大量對象不斷產生,對應的方法代碼肯定會被頻繁調用 , 占用的cpu必然較高,參考上一篇https://www.cnblogs.com/process-h/p/16879018.html
最終定位到的代碼如下:
@RestControllerpublic class IndexController {    @RequestMapping("/user/process")    public String processUserData() throws InterruptedException {        ArrayList<User> users = queryUsers();        for (User user: users) {            //TODO 業務處理            System.out.println("user:" + user.toString());        }        return "end";    }    /**     * 模擬批量查詢用戶場景     * @return     */    private ArrayList<User> queryUsers() {        ArrayList<User> users = new ArrayList<>();        for (int i = 0; i < 5000; i++) {            users.add(new User(i,"zhuge"));        }        return users;    }}public class User { private int id; private String name; // 1024B * 100 = 100KB byte[] a = new byte[1024*100]; public User(){} public User(int id, String name) {super();this.id = id;this.name = name; } public int getId() {return id;} public void setId(int id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;}}發現User類中定義了一個byte[] a 成員變量,占了100KB,在queryUsers()中,一次性在內存中添加了500M的對象數據,明顯不合適,需要根據上述中的運行時內存數據區域閾值進行優化 , 盡量消除這種朝生夕死的對象導致的full GC.

推薦閱讀