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.

推荐阅读