我们说jvm调优,其实就是不断测试调整jvm的运行参数,尽可能让对象都在新生代**(Eden)**里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁的进行垃圾回收。从而减少STW(stop the world)的时间。

调优思路

项目运行内存分析

​ 我们运行应用程序时,一般会设置一些jvm参数,比如堆内存大小,年轻代大小,Eden和Survivor的比例,老年代大小,大对象的阈值,大龄对象进入老年代的阈值等。

​ 而设置这些jvm参数,有2种方式:

  1. 通过物理内存分析设置,比如机器有8G内存,假设操作系统分配2-3G,元空间分配256M,堆分配4-5G。
  2. 通过1设置之后,再通过分析具体的gc日志来调优。

​ 我们知道jvm有自己的运行时数据区(内存模型),其中堆大小,以及堆中的年轻代、老年代的大小比例至关重要,主要就是调整堆中的内存比例,运行时数据区(内存模型)图,如下图:

具体思路

1、分析年轻代对象增长的速率

​ 可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。

2、Young GC的触发频率和每次耗时

​ 知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC 公式算出,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。

3、每次Young GC后有多少对象存活和进入老年代

​ 这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。

4、Full GC的触发频率和每次耗时

​ 知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。

**总结:**尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。

**注意:**对象进入老年代的几种方式:

  1. 大对象
  2. 对象到达一定年龄阈值
  3. 动态对象年龄判断(Young GC后的存活对象小于Survivor区域的50%)

调优案例

案例准备

​ 这里准备了一个示例程序(demo链接)

初始JVM参数:

-Xms1536M -Xmx1536M -Xmn512M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly

根据这些参数,我们知道大体的内存模型是这样的:最快经过6s之后才会发生一次Young GC

调优分析

示例程序启动后,我们调用测试类的test()方法:

@RunWith(SpringRunner.class)
@SpringBootTest(classes={Application.class})// 指定启动类
public class ApplicationTests {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}@Autowiredprivate RestTemplate restTemplate;@Testpublic void test() throws Exception {for (int i = 0; i < 10000; i++) {String result = restTemplate.getForObject("http://localhost:8080/user/process", String.class);Thread.sleep(1000);}}
}

然后观察整个过程前后,虚拟机的内存gc变化:

发现不仅Young GC次数增多了,Full GC的次数也随着增多,说明对象不仅增长得快,连进入老年代的时间挺快的。

我们回想一下对象进入老年代的几种方式:

  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参数是否生效,优化后的内存模型为:

优化后我们再重新跑一下程序,新的gc变化:

​ 优化完发现没什么变化,反而是Full GC次数还变多了。

我们思考下full gc 比minor gc还多的原因有哪些?

1、元空间不够导致的多余full gc

2、显示调用System.gc()造成多余的full gc,这种一般线上尽量通过-XX:+DisableExplicitGC参数禁用,如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果

3、老年代空间分配担保机制

可以简单排除掉前2个原因,第三个老年代空间担保机制也可以通过观察minor gc 与full gc的次数比例进行排除,那接下来就可能真的就是程序产生了很多占内存的对象。我们可以通过jmapjvisualvm来跟踪到占内存的对象。

jmap -histo 27808

查到了有大量User对象生成,这个可能是问题所在,但不确定,还必须找到对应的代码确认,如何找到对应的代码有如下几种方式:

1、代码里全文搜索生成User对象的地方(适合只有少数几处地方的情况)

2、如果生成User对象的地方太多,无法定位具体代码,我们可以同时分析下占用cpu较高的线程,一般有大量对象不断产生,对应的方法代码肯定会被频繁调用,占用的cpu必然较高,参考上一篇
https://www.cnblogs.com/process-h/p/16879018.html

最终定位到的代码如下:

@RestController
public 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 = 100KBbyte[] 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.

总结:到这里,调优案例就结束了,整个过程考虑了jvm的各个调优知识点,相信有心的读者可以学到一些知识点。

jvm调优思路及调优案例相关推荐

  1. JVM调优思路、订单秒杀jvm调优案例

    文章目录 1. jvm调优思路 2. 订单的秒杀模块jvm调优案例 1. jvm调优思路 jvm调优其实更多的是对GC的优化,尤其是尽量减少full GC. 大多数情况下,对象在Eden区分配,当Ed ...

  2. jvm调优五:jvm调优工具和调优实战

    jvm调优工具和调优实战 jvm自带常用命令 JPS jps是用于查看有权访问的hotspot虚拟机的进程id. 当未指定hostid时,默认查看本机jvm进程id -l:输出完整jar名称 -v:输 ...

  3. 大数据技术之_19_Spark学习_07_Spark 性能调优 + 数据倾斜调优 + 运行资源调优 + 程序开发调优 + Shuffle 调优 + GC 调优 + Spark 企业应用案例

    大数据技术之_19_Spark学习_07 第1章 Spark 性能优化 1.1 调优基本原则 1.1.1 基本概念和原则 1.1.2 性能监控方式 1.1.3 调优要点 1.2 数据倾斜优化 1.2. ...

  4. 谈谈你的GC调优思路?

    基本的调优思路可以总结为: 理解应用需求和问题,确定调优目标.假设,我们开发了一个应用服务,但发现偶尔会出现性能抖动,出现较长的服务停顿. 评估用户可接受的响应时间和业务量,将目标简化为,希望GC暂停 ...

  5. 2亿数据量PostgreSQL 10.4查询调优思路分享

    目录 ●背景 ●使用物理服务器 ●增加内存大小 ●使用NVMe协议的固态硬盘 ●将数据库安装在内存 ●业务调整 ●修改默认配置项 ●启用Gin (Generalized Inverted Index) ...

  6. JVM内存模型和性能调优:系列文章 - 导读

    0.JVM课程总体介绍 学习 Java 虚拟机能深入地理解 Java 这门语言,想要深入学习java的各种细节,很多时候你要深入到字节码层次去分析,你才能得到准确的结论,通过学习JVM你了解JVM历史 ...

  7. JVM之堆Heap参数调优入门

    JVM之堆Heap参数调优入门 目录: JVM体系结构概览 JVM之堆Heap参数调优入门 2.1 java7和 java8堆结构图 2.2 堆内存调优简介 1. JVM体系结构概览 2. JVM之堆 ...

  8. java虚拟机工作原理图_超“强”的图文详解-JVM虚拟机底层原理与调优实战

    今天我和大家分享一篇文章,文章上半部分为JVM底层原理 下半部分为调优实战 文章有点长,需要点耐心哦! 如果觉得看文章太难理解,就点击下面我投稿B站的jvm视频讲解. 还配有视频讲解:解密BATJ一线 ...

  9. Neo4j ③ 管理员操作, 备份恢复, 调优思路, 程序访问, 嵌入式, 服务器模式, Java 操作 Neo4j, 整合 SpringBoot

    目录 第四部分 Neo4j之Admin管理员操作 4.1 Neo4j - 数据库备份和恢复 4.2 调优思路 1.增加服务器内存 和 调整neo4j配置文件 2.neo4j刚启动数据是冷的需要预热 3 ...

最新文章

  1. 1.Maven之(一)Maven是什么
  2. 双系统 win10 时间不对
  3. 为何python不好找工作k-为什么我不建议你通过 Python 去找工作?
  4. HTTP/3 原理实战
  5. Win7下如何更改时间日期
  6. 操作系统存储器管理实验报告_献上膝盖!华为工程师抛出一份堪称“举世无双”操作系统笔记...
  7. 分享8个超棒的免费高质量图标搜索引擎
  8. python编程设计登录和注册程序_小白成长记-----python实现注册的小程序
  9. iisweb服务器完美解决方案
  10. 4-5 求自定类型元素的最大值 (10分)
  11. Caffe 的深度学习训练全过程
  12. Java的生成器模式(又名建造者模式),你真的会了吗
  13. 网页监控系统设计之使用mjpg-streamer
  14. 未明学院:云计算热门机器学习项目来袭!收割亚马逊、阿里等一众巨头都看重的实操技能与项目经历!
  15. 计算机音乐恋曲1990字谱,歌曲恋曲1990简谱
  16. 用户主页个性域名技术实现
  17. one 主格 复数 宾格_主格和宾格的复数形式到底怎么写请说出答案
  18. 论文阅读:An Empirical Study of Spatial Attention Mechanisms in Deep Networks
  19. Ajax上传文件的cache、processdata、contentType属性以及FormData对象的总结
  20. 免费的java开发工具_Java那些最常用的免费开放工具,分享这15个!

热门文章

  1. USB-KEY原理简要说明
  2. 计算机复试面试重点问题汇总
  3. 属性和方法的区别是什么_痤疮和痘痘有什么区别?长痘痘之后3种护肤方法,痤疮3种治疗方式...
  4. html中字体怎么写,fontfamily属性字体
  5. 小程序影藏溢出的gif_分享几个优质开源项目 | 电商类app,趣享 gif,研发助手DoraemonKit,github小程序...
  6. IDEA 开启内存显示与修改内存
  7. 【前辈经验之谈】发顶会论文,怎么就那么难?
  8. 英语口译笔记法实战指导 吴钟明 pdf_Adam12月口译课即将开课!还剩一个努力学习的你!...
  9. 为什么这么努力还这么贫穷?
  10. Unity之tvOS平台注意事项