1 调优原则
大多数的Java应用不需要进行JVM调优,一般项目加个xms和xmx参数就够了,JVM调优不是常规手段,性能问题一般第一选择是优化代码,最后的选择才是进行JVM调优,JVM优化是最后不得已的手段。
2 何时进行JVM调优
- Heap内存(老年代)持续上涨达到设置的最大内存值
- Full GC 次数频繁
- GC 停顿时间过长(超过1秒)
- 应用出现OutOfMemory 等内存异常
- 应用中有使用本地缓存且占用大量内存空间
- 系统吞吐量与响应性能不高或下降
3 JVM调优的步骤
- 监控GC的状态
使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,再决定是否进行优化。
当发生如下情况需要分析JVM内存快照dump:
- 每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,Full GC的时间也由之前的0.5s延长到4、5s
- Full GC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC
- 老年代的内存越来越大并且每次Full GC后年老代没有内存被释放
之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值
- 生成堆的dump文件
通过JMX的MBean生成当前的Heap信息的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。
- 分析dump文件
打开这个hprof文件,可以使用如下工具
- IBM HeapAnalyzer
- JDK 自带的jhat或者VisualVM
- Mat(Eclipse专门的静态内存分析工具)推荐使用
- 分析结果,判断是否需要优化
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化,如果GC时间超过1-3秒,或者频繁GC,则必须优化。
如果满足下面的指标,则一般不需要进行GC:
- Minor GC执行时间不到50ms
- Minor GC执行不频繁,约10秒一次
- Full GC执行时间不到1s
- Full GC执行频率不算频繁,不低于10分钟1次
- 调整GC类型和内存分配
如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行测试,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择。
- 不断的分析和调整
通过不断的试验和试错,分析并找到最合适的参数,如果找到了最合适的参数,则将这些参数应用到所有服务器。
4 JVM命令
jps
功能和linux中的ps命令类似:可以列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)。
jstat
jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据。
jmap
用于生成堆转储快照(一般称为heap dump或dump文件)
jhat
jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。
jstack
jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
5 可视化故障处理工具
- JConsole:Java监视与管理控制台
JConsole(Java Monitoring and Management Console)是一款基于JMX(Java Manage-ment Extensions)的可视化监视、管理工具。它的主要功能是通过JMX的MBean(Managed Bean)对系统进行信息收集和参数动态调整。
- VisualVM
- 显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)。
- 监视应用程序的处理器、垃圾收集、堆、方法区以及线程的信息(jstat、jstack)。
- dump以及分析堆转储快照(jmap、jhat)。
- 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。
- 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照,可以将快照发送开发者处进行Bug反馈。
- 其他插件带来的无限可能性。
下图为安装了Visial GC插件后的效果
6 JVM参数解析及调优
- 6.1 选择合适的垃圾回收器
- CPU单核,那么毫无疑问Serial 垃圾收集器是唯一的选择
- CPU多核,关注吞吐量 ,那么选择Parallel Scavenge+Parallel Old组合
- CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,那么选择CMS。
- CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。
//设置Serial垃圾收集器(新生代)
开启:-XX:+UseSerialGC
//设置PS+PO,新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器
开启 -XX:+UseParallelOldGC
//CMS垃圾收集器(老年代)
开启 -XX:+UseConcMarkSweepGC
//设置G1垃圾收集器
开启 -XX:+UseG1GC
- 6.2 调整内存大小
为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值。
//设置堆初始值
-Xms2g //初始值2G
//设置堆最大值
-Xmx2g //堆最大值2G
- 6.3 设置线程栈的大小
JDK 5.0版本以后每个线程栈大小为1 MB,用于存放栈帧、调用参数、局部变量等。JDK 5.0以前版本每个线程栈大小为256 KB。请依据应用的线程所需内存大小进行调整。在相同物理内存下,减小该值可以生成更多的线程。但是操作系统对一个进程内的线程个数有一定的限制,无法无限生成,一般在3000个~5000个。
-Xss128k //设置每个线程的栈大小为128 KB
- 6.4 调整内存区域大小比例
年轻代和年老代将默认的比例为1:2,可以通过调整二者之间的比例来调整二者之间的大小,也可以显式指定年轻代大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize
-XX:MaxNewSize设置为同样大小。
//新生代和老年代的占比
-XX:NewRatio=4 //表示新生代:老年代 = 1:4 即老年代占整个堆的4/5
//新生代内存大小
-Xmn20M //表示设置年轻代的大小为20M
-XX:NewSize20M //设置年轻代大小为20M
-XX:MaxNewSize20M //设置年轻代最大值为20M
//survivor区和Eden区大小比率
-XX:SurvivorRatio=8 //表示设置2个Survivor区:1个Eden区的比值为2:8,这意味着Survivor区占整个年轻代的1/5,这个参数默认为8
- 6.5 调整对象升老年代的年龄
-XX:MaxTenuringThreshold=7 //表示对象年龄大于7,自动进入老年代
- 6.6 调整大对象的标准
-XX:PretenureSizeThreshold=3145728 //表示对象大于3145728(3M)时直接进入老年代分配,这里只能以字节作为单位
6.7 设置元空间初始大小以及最大可分配大小(JDK8及以后)
-XX:MetaspaceSize=100m //设置元空间初始大小为100M
-XX:MaxMetaspaceSize=100m //设置元空间最大可分配大小为100M
- 6.8 用于辅助的GC典型配置参数
-XX:+PrintGC //用于输出GC日志
-XX:+PrintGCDetails //用于输出详细GC日志
-XX:+PrintGCTimeStamps //用于输出GC时间戳(JVM启动到当前日期的总时长的时间戳形式)
-XX:+PrintGCDateStamps //用于输出GC时间戳(日期形式)
-XX:+PrintHeapAtGC //在进行GC前后打印出堆的信息
-Xloggc:../logs/gc.log //日志文件的输出路径
7 IDEA中如何设置jvm参数
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/71485.html