JVM性能调优的原则有哪些?
多数的Java应用不需要在服务器上进行GC优化,虚拟机内部已有很多优化来保证应用的稳定运行,所以不要为了调优而调优,不当的调优可能适得其反
在应用上线之前,先考虑将机器的JVM参数设置到最优(适合)
在进行GC优化之前,需要确认项目的架构和代码等已经没有优化空间。我们不能指望一个系统架构有缺陷或者代码层次优化没有穷尽的应用,通过GC优化令其性能达到一个质的飞跃
GC优化是一个系统而复杂的工作,没有万能的调优策略可以满足所有的性能指标。GC优化必须建立在我们深入理解各种垃圾回收器的基础上,才能有事半功倍的效果
处理吞吐量和延迟问题时,垃圾处理器能使用的内存越大,即java堆空间越大垃圾收集效果越好,应用运行也越流畅。这称之为GC内存最大化原则
在这三个属性(吞吐量、延迟、内存)中选择其中两个进行jvm调优,称之为GC调优3选2
————————————————
什么情况下需要JVM调优?
Heap内存(老年代)持续上涨达到设置的最大内存值
Full GC 次数频繁
GC 停顿(Stop World)时间过长(超过1秒,具体值按应用场景而定)
应用出现OutOfMemory 等内存异常
应用出现OutOfDirectMemoryError等内存异常( failed to allocate 16777216 byte(s) of direct memory (used: 1056964615, max: 1073741824))
应用中有使用本地缓存且占用大量内存空间
系统吞吐量与响应性能不高或下降
应用的CPU占用过高不下或内存占用过高不下
————————————————
在JVM调优时,你关注哪些指标?
**吞吐量:**用户代码时间 / (用户代码执行时间 + 垃圾回收时间)。是评价垃圾收集器能力的重要指标之一,是不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用程序达到的最高性能指标。吞吐量越高算法越好。
**低延迟:**STW越短,响应时间越好。评价垃圾收集器能力的重要指标,度量标准是缩短由于垃圾收集引起的停顿时间或完全消除因垃圾收集所引起的停顿,避免应用程序运行时发生抖动。暂停时间越短算法越好
在设计(或使用)GC 算法时,我们必须确定我们的目标:一个 GC 算法只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折衷
MinorGC尽可能多的收集垃圾对象。我们把这个称作MinorGC原则,遵守这一原则可以降低应用程序FullGC 的发生频率。FullGC 较耗时,是应用程序无法达到延迟要求或吞吐量的罪魁祸首
堆大小调整的着手点、分析点:
统计Minor GC 持续时间
统计Minor GC 的次数
统计Full GC的最长持续时间
统计最差情况下Full GC频率
统计GC持续时间和频率对优化堆的大小是主要着手点
我们按照业务系统对延迟和吞吐量的需求,在按照这些分析我们可以进行各个区大小的调整
一般来说吞吐量优先的垃圾回收器:-XX:+UseParallelGC -XX:+UseParallelOldGC,即常规的(PS/PO)
响应时间优先的垃圾回收器:CMS、G1
————————————————
JVM 优化步骤
分析和定位当前系统的瓶颈
对于JVM的核心指标,我们的关注点和常用工具如下:
1)CPU指标
查看占用CPU最多的进程
查看占用CPU最多的线程
查看线程堆栈快照信息
分析代码执行热点
查看哪个代码占用CPU执行时间最长
查看每个方法占用CPU时间比例
// 显示系统各个进程的资源使用情况
top
// 查看某个进程中的线程占用情况
top -Hp pid
// 查看当前 Java 进程的线程堆栈信息
jstack pid
常见的工具:
jdk自带的jvm监控工具:位于jdk的bin目录下的Jconsole,JvirtualVM,JMAP;但是遇见大一点的程序就容易死机,因为本身就是用 java写的,所以有占据本机的JVM内存,所以一般是远程检测,故对于大程序一般不推荐直接使用,而推荐使用jmap+mat。
商用的有JProfiler、JVM Profiler、Arthas等;使用简单,能调试大型程序
2)JVM 内存指标
查看当前 JVM 堆内存参数配置是否合理
查看堆中对象的统计信息
查看堆存储快照,分析内存的占用情况
查看堆各区域的内存增长是否正常
查看是哪个区域导致的GC
查看GC后能否正常回收到内存
常见的命令:
// 查看当前的 JVM 参数配置
ps -ef | grep java
// 查看 Java 进程的配置信息,包括系统属性和JVM命令行标志
jinfo pid
// 输出 Java 进程当前的 gc 情况
jstat -gc pid
// 输出 Java 堆详细信息
jmap -heap pid
// 显示堆中对象的统计信息
jmap -histo:live pid
// 生成 Java 堆存储快照dump文件
jmap -F -dump:format=b,file=dumpFile.phrof pid
常见的工具:Eclipse MAT、JConsole等。
3)JVM GC指标
查看每分钟GC时间是否正常
查看每分钟YGC次数是否正常
查看FGC次数是否正常
查看单次FGC时间是否正常
查看单次GC各阶段详细耗时,找到耗时严重的阶段
查看对象的动态晋升年龄是否正常
JVM 的 GC指标一般是从 GC 日志里面查看,默认的 GC 日志可能比较少,我们可以添加以下参数,来丰富我们的GC日志输出,方便我们定位问题。
GC日志常用 JVM 参数:
// 打印GC的详细信息
-XX:+PrintGCDetails
// 打印GC的时间戳
-XX:+PrintGCDateStamps
// 在GC前后打印堆信息
-XX:+PrintHeapAtGC
// 打印Survivor区中各个年龄段的对象的分布信息
-XX:+PrintTenuringDistribution
// JVM启动时输出所有参数值,方便查看参数是否被覆盖
-XX:+PrintFlagsFinal
// 打印GC时应用程序的停止时间
-XX:+PrintGCApplicationStoppedTime
// 打印在GC期间处理引用对象的时间(仅在PrintGCDetails时启用)
-XX:+PrintReferenceGC
以上就是我们定位系统瓶颈的常用手段,大部分问题通过以上方式都能定位出问题原因,然后结合代码去找到问题根源。
确定优化目标
定位出系统瓶颈后,在优化前先制定好优化的目标是什么,例如:
-
将FGC次数从每小时1次,降低到1天1次
-
将每分钟的GC耗时从3s降低到500ms
-
将每次FGC耗时从5s降低到1s以内
制订优化方案
针对定位出的系统瓶颈制定相应的优化方案,常见的有:
-
代码bug:升级修复bug。典型的有:死循环、使用无界队列。
-
不合理的JVM参数配置:优化 JVM 参数配置。典型的有:年轻代内存配置过小、堆内存配置过小、元空间配置过小。
对比优化前后的指标,统计优化效果
持续观察和跟踪优化效果
如果还需要的话,重复以上步骤