小白也能轻松搞懂的JVM调优方法(入门)

263 阅读4分钟

理论知识

触发 GC 原因

Minor GC

  • 年轻代空间不足(指的是Eden,Survivor满不会触发GC)
  • 老年代空间不足可能会触发 Minor GC

Major GC

  • 老年代空间不足

Full GC

  • 老年代空间不足
  • 方法区空间不足
  • 调用 System.gc()时(建议执行 Full GC,不是绝对)
  • Minor GC 后进入老年代对象的平均大小 > 老年代的可用内存
  • 产生的对象 > 老年代的可用内存

OOM原因

  • 内存泄露

程序中的对象不会再使用了,且GC不能进行回收。

  • 内存溢出

1、JVM堆内存设置不合理
2、 程序中创建了大量对象,并且长时间不能被GC回收
3、内存泄露会导致内存溢出

调优目的

GC优化的目的有两个

  • 将转移到老年代的对象数量降低到最小
  • 减少 Full GC 的执行频率和执行时间

调优工具

小编在这列举几个常用的调优工具,挑选两三个作为平时常用的即可。

  • JDK命令行
  • Jconsole
  • JvisualVM
  • MAT
  • Jprofiler
  • GCViewer
  • GCEasy

调优步骤

JVM调优简要概括为三部曲,先要对内存进行监控,了解内存中的实际情况,再对监控的数据结果进行分析,经过一系列分析后,再根据分析结果判断是否需要优化,思考如何优化等。

监控

调优过程的第一步,下面列举一些使用各种JVM工具需监控的数据(当然不只这些数据)

1、监控宿主机 cpu,内存 等状态信息
2、jdk版本
3、查看系统日志
4、了解JVM参数设置
5、下载 dump 文件

  • 建议添加参数:-XX:HeapDumpOnOutOfMemoryError,出现OOM时下载dump文件;并且指定 dump 文件地址,参数:-XX:HeapDumpPath=<path>

6、GC频率时间等信息
7、各线程状态以及执行情况
8、大对象的占比,实例数等

分析

根据当前监控到的程序的情况以及设置的程序JVM参数,分析是否需要进行优化。如果各项参数设置合理,GC频率不高,GC耗时不高,系统没有出现异常情况等则不需要进行优化。

反之,如果出现以下情况,则需要进行调优:

  • Minor GC / Full GC执行时间长(超过1-3s)
  • Minor GC / Full GC执行频率高(10min以内/次)
  • 每次 Major GC 结束老年代内存占用曲线呈上升趋势(有内存泄露的可能)

如何调优

调优就是不断的实验和试错的过程,经过分析并找到最合适的参数和设置。调优需遵循的原则如下:

  • 频繁 Minor GC
  • 较少 Major GC
  • 基本不动 Metaspace

JVM调优参数参考

  • 减少使用全局对象和大对象

  • 调整新生代和老年代的比例到最合适,什么比例才最合理?

    1)更大的年轻代必然导致更小的老年代,大的年轻代会延长YGC的周期,但会增加每次 YGC 的时间;小的老年代会导致更频繁的 Full GC
    2)更小的年轻代必然导致更大的老年代,小的年轻代会导致YGC频繁,但每次YGC时间会更短,大的老年代会减少 Full GC 的频率
    如何选择应该依赖应用程序对象生命周期的分布情况,如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,老年代应适当增大。

  • 选择合适的GC收集器

    根据主机cpu的核数选择串行/并行的GC收集器

  • 针对JVM堆的设置,一般通过 -Xms -Xmx 设置初始堆大小和最大堆大小,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把 -Xms -Xmx 设置为相同的值

列举常见问题的解决思路

1、cpu load 过高

  • 找出哪个进程cpu占用高(top)
  • 找出该进程的哪个线程cpu占用高(top -H -p pid)
  • 把进程号转 16 进制(printf %x pid)
  • 打印出错的线程堆栈信息(jstack <pid> | grep <thread-hex-id> -A 10,其中 -A 10 参数用来指定显示行数)

重点关注(WAITING/BLOCKED)找到线程中 waiting on <xx> 的有哪些,找到是哪个线程持有 <xx> 这把锁。

查看线程中是否出现(deadlock)死锁,死锁和频繁的 full gc 往往是导致 cpu load 飙升的原因。

2、系统内存占比过高

  • 导出堆内存dump(jmap)
  • 使用jvm工具分析