文章目录

  • 1 调优原则
  • 2 何时进行JVM调优
  • 3 JVM调优的步骤
  • 4 JVM命令
  • 5 可视化故障处理工具
  • 6 JVM参数解析及调优


1 调优原则

大多数的Java应用不需要进行JVM调优,一般项目加个xms和xmx参数就够了,JVM调优不是常规手段,性能问题一般第一选择是优化代码,最后的选择才是进行JVM调优,JVM优化是最后不得已的手段。

2 何时进行JVM调优

  • Heap内存(老年代)持续上涨达到设置的最大内存值
  • Full GC 次数频繁
  • GC 停顿时间过长(超过1秒)
  • 应用出现OutOfMemory 等内存异常
  • 应用中有使用本地缓存且占用大量内存空间
  • 系统吞吐量与响应性能不高或下降

3 JVM调优的步骤

  1. 监控GC的状态

使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,再决定是否进行优化。
当发生如下情况需要分析JVM内存快照dump:

  • 每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,Full GC的时间也由之前的0.5s延长到4、5s
  • Full GC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC
  • 老年代的内存越来越大并且每次Full GC后年老代没有内存被释放

之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值

  1. 生成堆的dump文件

通过JMX的MBean生成当前的Heap信息的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。

  1. 分析dump文件

打开这个hprof文件,可以使用如下工具

  • IBM HeapAnalyzer
  • JDK 自带的jhat或者VisualVM
  • Mat(Eclipse专门的静态内存分析工具)推荐使用
  1. 分析结果,判断是否需要优化

如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化,如果GC时间超过1-3秒,或者频繁GC,则必须优化。
如果满足下面的指标,则一般不需要进行GC:

  • Minor GC执行时间不到50ms
  • Minor GC执行不频繁,约10秒一次
  • Full GC执行时间不到1s
  • Full GC执行频率不算频繁,不低于10分钟1次
  1. 调整GC类型和内存分配

如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行测试,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择。

  1. 不断的分析和调整

通过不断的试验和试错,分析并找到最合适的参数,如果找到了最合适的参数,则将这些参数应用到所有服务器。

4 JVM命令

java内存优化工具 java内存高要优化_java内存优化工具


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)对系统进行信息收集和参数动态调整。

java内存优化工具 java内存高要优化_java内存优化工具_02


java内存优化工具 java内存高要优化_调优_03

  • VisualVM
  • 显示虚拟机进程以及进程的配置、环境信息(jps、jinfo)。
  • 监视应用程序的处理器、垃圾收集、堆、方法区以及线程的信息(jstat、jstack)。
  • dump以及分析堆转储快照(jmap、jhat)。
  • 方法级的程序运行性能分析,找出被调用最多、运行时间最长的方法。
  • 离线程序快照:收集程序的运行时配置、线程dump、内存dump等信息建立一个快照,可以将快照发送开发者处进行Bug反馈。
  • 其他插件带来的无限可能性。

java内存优化工具 java内存高要优化_JVM_04


下图为安装了Visial GC插件后的效果

java内存优化工具 java内存高要优化_JVM_05

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参数

java内存优化工具 java内存高要优化_java内存优化工具_06


java内存优化工具 java内存高要优化_调优_07


java内存优化工具 java内存高要优化_java内存优化工具_08


java内存优化工具 java内存高要优化_调优_09