jvm调优详细教程及多个实战案例(建议收藏)

0.前言

建议与我的八股专栏的jvm八股配合使用。觉得本贴有用的收藏点个赞评论下送朵花什么的吧。下面我会让你看看什么是传说中的jvm调优(八股面试吹b版)。

打个小广告

我的架构设计专栏:https://www.nowcoder.com/creation/manager/columnDetail/0ybvLm

我的八股专栏:https://www.nowcoder.com/creation/manager/columnDetail/j8ZZk0

内有详细苍穹外卖话术哦!

为什么我要开个八股专栏?

一方面有感于现在面试找工作对八股的要求之高,一方面有感于市面上的一些八股资料又乱又复杂难懂,所以我决定自己结合各种市面上优秀的博客、gpt,牛客上的面经和我自己的理解,整合出一份八股资料,我的目标是:让八股文成为真正简单易懂的八股,知识点丰富且浅显易懂,不追求过分的深入,但一定重点层次分明(如果想深入了解知识点的话还是建议自己认真看看javaguide和小林coding,但其实我就是看了他们的全部博客取精华精炼部分融入我的专栏。。。),一句话就是我的八股专栏主打一个面试速成,一点超纲的知识点都不会有,因为我自己也用这份笔记准备秋招。。。

1.调优原则

JVM调优应该是Java性能优化的最后一颗子弹。性能问题一般第一选择是优化程序,最后的选择才是进行JVM调优。

2.JVM调优时机

不得不考虑进行JVM调优的是那些情况呢?

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

3.JVM调优目标

  • 减少STW,提升用户体验
  • 降低内存占用

吞吐量、延迟、内存占用三者类似CAP,构成了一个不可能三角,只能选择其中两个进行调优,不可三者兼得。

  • 延迟:GC低停顿和GC低频率;
  • 低内存占用;
  • 高吞吐量;

选择了其中两个,必然会会以牺牲另一个为代价。

下面展示了一些JVM调优的量化目标参考实例:

  • Heap 内存使用率 <= 70%;
  • Old generation内存使用率<= 70%;
  • avgpause <= 1秒;
  • Full gc 次数0 或 avg pause interval >= 24小时 ;

注意:不同应用的JVM调优量化目标是不一样的。

4.JVM调优的步骤

一般情况下,JVM调优可通过以下步骤进行:

  • 分析系统系统运行情况:分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;
  • 确定JVM调优量化目标;
  • 确定JVM调优参数(根据历史JVM参数来调整);
  • 依次确定调优内存、延迟、吞吐量等指标;
  • 对比观察调优前后的差异;
  • 不断的分析和调整,直到找到合适的JVM参数配置;
  • 找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

jvm常用命令

top命令找到消耗 CPU 很高的进程 id

top命令用于实时监控系统进程和资源占用情况

PID		进程号
USER		用户名
PR		优先级
NI		Nice值
VIRT		虚拟内存
RES		常驻内存
SHR		共享内存
S		当前状态
%CPU		占用CPU百分比
%MEM		占用内存百分比
TIME+		运行时长
COMMAND		启动命令

top -p 4337 显示目标进程信息

然后在该界面输入 H,则展示该进程下所有的线程信息,如下:

jps查看java进程

输出JVM中运行的进程状态信息

jps -l :查看所有进程号;

jstack对当前的进程做 dump,输出所有的线程信息

查看java进程内线程的堆栈信息。

jstack [option] <pid>  

使用jstack查看进行堆栈运行信息

可以看到它会输出所有线程的堆栈信息,并且存在一些 16 进制的 nid。

把上面得到的cpu利用率恶高的线程 id 4340,4339进行 16 进制转换:

 4340---10F4
 4339---10F3

把转换的进制在第 4 步的线程信息中查找,如下:

从图中可以看出,造成 CPU100% 的原因是在由于垃圾回收线程导致的,而可能不是我们业务线程导致的。

jinfo查看jvm参数

jinfo -flags [port]

jstat 查看 GC 情况

主要用来查看JVM运行时的状态信息,包括内存状态、垃圾回收等。

是JVM统计监测工具。可以用来显示垃圾回收信息、类加载信息、新生代统计信息等。

jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。

常见参数

①总结垃圾回收统计

jstat -gcutil pid

字段

含义

S0

幸存1区当前使用比例

S1

幸存2区当前使用比例

E

伊甸园区使用比例

O

老年代使用比例

M

元数据区使用比例

CCS

压缩使用比例

YGC

年轻代垃圾回收次数

YGCT

年轻代垃圾回收消耗时间

FGC

老年代垃圾回收次数

FGCT

老年代垃圾回收消耗时间

GCT

垃圾回收消耗总时间

②垃圾回收统计

//每间隔 2s 总共查询 10 次 GC 情况
jstat -gc 4337 2000 10

S0C:第一个幸存区(From 区)的大小
S1C:第二个幸存区(To 区)的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园(Eden)区的大小
EU:伊甸园(Eden)区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小 
CCSU:压缩类空间使用大小 
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

从上图可以看出,YoungGC 只进行了 19 次,而进行了大量的 Full GC(7000多次且继续递增),同时PrintGC还显示抛出了 OutOfMemory

结论:这里的 CPU 占用过高不是业务线程导致,而是 GC 线程占用过高导致的,JVM 在疯狂的进行垃圾回收,JVM 中默认的垃圾回收器是多线程的,所以多线程在疯狂回收,导致 CPU 占用过高。

jmap生成堆转储快照,执行一下命令打印堆内存相关信息

它为什么会进行疯狂的垃圾回收?很显然,内存不够,我们通过jmap看一下堆的内存情况,如下:

jmap –heap 4337

很明显的可以看到,老年代已经被塞得满满的了,也就是说 GC 好像没有作用了,有人说为什么没有自动扩容(代码中有注释,设置了 -Xmx=200M,目的是快速测试),就算没有设置,随着时间最后也会塞满。

因此,问题的根本很可能发生了内存泄漏。

我们可以通过 jmap导出堆的快照(即 dump 文件),但是导出 dump 文件过大会对程序造成影响,因此我们可以先jmap -histo pid,它可以打印每个 class 的实例数目,内存占用,类全名信息。如下:

//把JVM中的对象全部打印出来, 但是这样太多了,那么我们选择前 20 的对象展示出来
jmap –histo 4337 | head -20

一般来说,前面这几行,就可以看出,到底是哪些对象占用了内存。我们知道对象都是朝生夕死的,那么为什么这些对象不死呢?回收不掉呢?其结果导致了 FullGCOutOfMemory

我们再回顾上面的代码,我们把线程池的最大线程数设置为 50,而我们的任务数为 100,任务数多于线程数,那么任务会进入阻塞队列,由于任务数一直多于线程数,所以每 0.1s 就会有 50 个任务进入阻塞队列,没有执行,但同时这些对象又被线程池对象 Executor 引用,且Executor 是一个 GCroots,所以堆中有 60 万个对象(UserInfo),阻塞队列中有 60 万个任务(ScheduledFutureTask),并且这些对象还无法回收。

因此,我们可以定位到代码,要么加大线程池的线程数,要么减少批量任务数

jvm调优的策略

选择合适的垃圾回收器

  • CPU 单核:那么

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java抽象带蓝子的笔记专栏 文章被收录于专栏

我的笔记专栏,内有自己整理的八股知识笔记和算法刷题笔记,我会不断通过他人和自己的面经来更新和完善自己的八股笔记。专栏每增加一篇文章费用就会上涨一点,如果你喜欢的话建议你尽早订阅。内有超详细苍穹外卖话术!后续还会更新其他项目和我的实习经历的话术!敬请期待!

全部评论
跟着篮子哥学
点赞 回复 分享
发布于 08-24 12:18 浙江

相关推荐

点赞 评论 收藏
分享
11 56 评论
分享
牛客网
牛客企业服务