调什么
JVM调优:1、是想调什么,2、能调什么(只能调开放接口的,很多东西不一定开放接口给你调整),综合考虑只有如下两个方面:
- 内存方面
- 线程方面
内存方面
-
JVM需要的内存总大小。
-
各块内存分配,新生代、存活区、老年代。
-
选择合适的垃圾回收算法、控制GC停顿次数和时间。
-
解决内存泄漏的问题,辅助代码优化。
-
内存热点:检查哪些对象在系统中数量最大,辅助代码优化。
线程方面
-
死锁检查,辅助代码优化。
-
Dump线程详细信息:查看线程内部运行情况,查找竞争线程,辅助代码优化。
-
CPU热点:检查系统哪些方法占用了大量CPU时间,辅助代码优化。
如何调优
-
监控JVM的状态,主要是内存、线程、代码、I/O几部分。
-
分析结果,判断是否需要优化。
-
调整:垃圾回收算法和内存分配,修改并优化代码。
-
不断的重复监控、分析和调整,直至找到优化的平衡点。
JVM调优的目标
-
GC的时间足够小。
-
GC的次数足够小。
-
将转移到老年代的对象数量降低到最小。
-
减少Full GC的执行时间。
-
发生Full GC的间隔时间足够的长。
常见的调优策略
-
减少创建对象的数量。
-
减少使用全局变量和大对象。
-
调整新生代、老年代的大小到最合适。
-
选择合适的GC收集器,并设置合理的参数。
JVM调优冷思考
-
多数的Java应用不需要在服务器上进行GC优化。
-
多数导致GC问题的Java应用,都不是因为参数设置错误,而是代码问题。
-
在应用上线前,先考虑将机器的JVM参数设置到最优(最合适)。
-
JVM优化是到最后不得已才采用的手段。
-
在实例使用中,分析JVM情况优化代码比优化JVM本身要多得多。
-
如下情况通常不用优化:
- Minor GC执行时间不到50ms。
- Minor GC执行不频繁,约10秒甚至更差时间才执行一次。
- Full GC执行时间不到1s。
- Full GC执行频率不算频繁,不低于10分钟1次。
JVM调优经验
-
要注意32位和64为操作系统的区别,32位操作系统支持的内存理论上最多为2的32次方4G,而64位操作系统理论的寻址能力为2的64次方,内存识别的多少是和计算机cpu的寻址有关。
-
注意client模式和Server模式的选择。
-
要想GC的时间小必须要一个更小的堆,而要保证GC次数足够少,又必须要保证一个更大的堆,这两个是有冲突的,只能取其平衡。
-
针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值。
-
新生代和老年代将根据默认的比例(1:2)分配内存堆,可以通过调整二者之间的比率NewRadio来调整,也可以通过-XX:newSize -XX:MaxNewSize来设置其绝对大小,同样,为了防止新生的堆收缩,通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。
-
合理规划新生代和老年代的大小。
-
如果应用存在大量的临时对象,应该选择更大的新生代;如果存在相对较多的持久对象,老年代应该适当增大。在抉择时应该本着Full GC尽量少的原则,让老年代尽量缓存常用对象,JVM的默认比例1:2也是这个道理。
-
通过观察应用一段时间,看其在峰值时老年代会占多少内存,在不影响Full GC的前提下,根据实际情况加大新生代,但应该给老年代至少预留1/3的增长空间。
-
线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太大了,一般256K就足够了。在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程。
内存泄漏
-
内存泄漏导致系统崩溃前的一些现象,比如:
- 1、每次垃圾回收时间越来越长,Full GC时间也延长到好几秒。
- 2、Full GC的次数越来越多,最频繁时隔不到1分钟就进行一次Full GC。
- 3、老年代的内存越来越大,并且每次Full GC后老年代没有内存被释放。
- 4、老年代堆空间被占满的情况。
-
内存泄漏的解决方式:一般就是根据垃圾回收前后情况对比,同时根据对象引用情况分析,辅助去查找泄漏点。
-
堆栈溢出的情况,通常会抛出java.lang.StackOverflowError,一般就是递归调用没退出,或者循环调用导致。
调优步骤
重点是调优的过程、方法和思路
内存调整、数据库连接调整、内存泄漏查找等
通过JMC录制JFR飞行记录
-
1、查看CPU的占用率
-
2、内存的分配和使用量
-
3、垃圾收集的执行频率、暂停时间、垃圾收集区域
-
4、文件I/O、套接字I/O,查看远程I/O阻塞时间
-
一般信息,堆使用量、CPU总体占用率、GC暂停时间是非常重要的三个指标,对于Java应用而言,GC暂停时间是最值得关注的指标。
-
通过内存信息,我们可以清晰的看到垃圾收集器的类型,垃圾收集的暂停时间,包括最短暂停时间、平均暂停时间、最长暂停时间,以及更为重要的垃圾收集频率(垃圾收集的周期及STW时长)。
-
代码分析是Java性能分析重点,通过代码分析,我们可以清楚的知道系统运行时,哪些类及方法被高频率的调用。
-
热点方法,通过查看热点方法调用栈,我们可以清晰的了解到系统的主要计算资源消耗情况。
-
通过调用树,我们能以模块化的方式直观的看到系统运行状态。
-
通过线程概述报告,我们可以得知CPU占用率的分布(系统占用率、应用程序+JVM占用率)和活动线程数,对于CPU占用率而言,应用程序应该占用99%的计算资源,而活动线程数应该控制在合理范围内(具体看应用)。
-
热点线程一栏,详细列出了热点线程的数量及详情,通过详情,我们可以得知线程的执行情况。
-
线程争用是解决应用性能最为关键的部分,在应用上线初期,我们可以通过解决线程争用初步实现系统性能的巨大提升。上图中的争用为GC导致,具体是由于使用G1时,设置的GC预期暂停时间过短导致的。
-
IO作为系统的基础指标,IO过高会导致系统性能急剧下降,避免过度打印日志和生成大文件可以避免系统IO过高导致的性能问题。
通过VisualVM查询实时的虚拟机信息
-
1、查看监视界面,可以看到cup运行情况、堆的使用情况、类的情况以及线程的动态情况。
-
2、查看线程,可以看到所有的线程的情况,是运行、休眠、等待、驻留、监视等情况。
也可以点击右上角Dump按钮,将线程的信息导出,其实就是执行的jstack命令。
- 3、查看抽样器,抽样器可以对CPU、内存在一段时间内进行抽样,
- A、CPU监控:查看热点方法和各个方法占用CPU时间及其比例。
- B、内存监控:每个线程分配内存。
抽样cpu
-
4、添加插件Visual GC,利用Visual GC分析虚拟机内存区域。
- A、内存大小分情况
- B、主要关注 GC Time长短及间隔
- C、查看是否old Gen,Eden是否存在持续上升
-
5、添加插件Tracer,Tracer可以监控Heap、PerGen、Cleass、Treads等等
总结:
-
通过JMC录制JFR飞行记录和通过VisualVM监控实时的虚拟机信息,进行调整优化。
-
针对性的去调整JVM内存、新生代、年代的空间大小、数据库连接池大小、MySQL最大连接数等。
-
不断的分析和调整,直到找到合适的JVM参数配置,找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。
参考: https://www.cnblogs.com/xifengxiaoma/p/9402263.html
https://my.oschina.net/yygh/blog/650507