【面试必问系列】JVM调优你会吗?

JVM 调优分类及思路分析:

调优是一个很大的概念,简单说就是把系统进行优化,但是站在一个系统的角度,能够干的事情太多了,一般调优的思路可以是“测试 - 分析 - 调优”三步走。任何调优一般都需要结合场景,明确已知问题和性能目标,比如这个服务就注重吞吐量,这个服务注重用户的体验(用户的响应时间)等等。不能为了调优而调优,引入不必要的问题。
常见的我们一般把 JVM 调优分成以下三类

  • JVM 预调优
  • 优化 JVM 运行环境(慢、卡顿等)
  • 解决 JVM 中的问题(OOM 等)

常见的jvm优化的一般思路

  1. 程序优化,效果通常非常大;
  2. 扩容,硬件采购等,如果金钱的成本比较小,不要和自己过不去;
  3. 参数调优,在成本、吞吐量、延迟之间找一个平衡点。

一、JVM 预调优

预调优一般有一定的量化的指标,比如吞吐量,响应时间,服务器资源,网络资源等等,然后根据指标可以依次计算内存需求、选定合适的cpu、选择合适的垃圾回收器、设定相应的新生代大小、分代年龄、设定日志参数等等一系列操作,进行一定的压测分析,得出优化结论和参数。

示例:

1、内存计算

内存不是越大越好,内存小,回收速度快也能承受,所以合理才是最好的。元空间(方法区)保险起见还是设定一个最大的值(默认情况下元空间是没有大小限制的),是为了保证堆空间扩展时有足够的的大小进行实际扩展。根据内存计算对象数据分析,进而能对GC 预估,进行优化(多久会满掉出发GC)。

2、垃圾回收器的选择

对于吞吐量优先的场景,就是使用 PS 组合(Parallel Scavenge+Parallel Old )
对于响应时间优先的场景,在 JDK1.8 的话优先 G1,其次是 CMS 垃圾回收器。

3、 新生代大小选择

对于响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,新生代收集发生的频率也是最小的.同时,减少到达老年代的对象.。

对于吞吐量优先的应用:一般都有一个很大的新生代和一个较小的老年代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象。当新生代设置过小时会导致:

  • ①MinorGC 次数更加频繁
  • ②可能导致 MinorGC 对象直接进入老年代,如果此时老年代满了,会触发 FullGC。
4、设定一些日志参数

-XX:+PrintGC 输出 GC 日志
-XX:+PrintGCDetails 输出 GC 的详细日志
-XX:+PrintGCTimeStamps 输出 GC 的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出 GC 的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行 GC 的前后打印出堆的信息
-Xloggc:…/logs/gc.log 日志文件的输出路径

二、优化 JVM 运行环境

常见的场景:一个是 CPU 占用过高,一个是内存占用过高。

1、CPU 占用过高排查思路

1) top
先通过 top 命令找到消耗 cpu 很高的进程 id 假设是 2732
在这里插入图片描述

2)top -Hp 进程ID
top -Hp 2732显示该进程下线程运行信息列表。
在这里插入图片描述

3)printf
将得到的线程编号 2734 转成 16 进制(0xaae),因为堆栈里线程id是16进制表示的。可以用程序员计算器,也可以用命令printf 工具:

printf "%x\n%" 2734`
0xaae

4)jstack
执行 jstack对当前的进程做 dump,输出所有的线程信息,查看堆栈,找到线程到底在干嘛。

jstack 2732(进程ID) | grep '0xaae'(线程ID) -C5 --color

在这里插入图片描述

5)解读线程信息,定位具体代码位置
可以分析是业务线程的问题,还是GC的问题。
这个里面 CPU 占用过高是 GC 线程占用过高导致的。

小结:如果 CPU 的 100%,要从两个角度出发,一个有可能是业务线程疯狂运行,比如说像很多死循环。还有一种可能性,就是 GC 线程在疯狂的回收,因为 JVM 中垃圾回收器主流也是多线程的,所以很容易导致 CPU 的 100%。

2、内存占用过高->OOM

1)思路:在遇到内存占用过高或者内存溢出的问题,一般情况下我们要首先查看系统中哪些对象占用得比较多,最终可能会OOM内存溢出。在实际的业务代码中,找到对应的对象,分析对应的类,找到为什么这些对象不能回收的原因,结合可达性分析算法,JVM 的内存区域,还有垃圾回收器等知识分析原因。

2)可以把 JVM 中的对象前 20 的对象展示出来,可以看到哪些对象占用了较高内存:

jmap –histo 1196 | head -20

小结:内存简单时,jmap –histo可以简单分析,如果内存复杂,则需通过 jmap 命令把整个堆内存的数据 dump 下来,通过内存分析工具进一步分析。

3)常见问题分析

  • 是否创建了很多超大对象
  • 超过预期访问量
  • 内存泄漏,大量对象引用没有释放等
  • 长生命周期的对象持有短生命周期对象的引用
  • 如数据库连接、网络连接和 IO 连接等未关闭
  • 变量作用域不合理
  • 内部类持有外部类
  • Hash 值改变
  • 直接内存的泄漏

4)直接内存泄漏

按照之前的常规的排查流程,是使用 top 命令来跟踪,然后使用 jmap –heap 来显示,发现占据的堆空间是比较小的,合计数远远小于 top 命令看到的,怀疑是虚拟机栈,于是使用 jstack 命令来看下线程,发现线程也占用不了那么多内存,然后jmap -histo 3468 | head -20 显示占用内存最多的对象,发现这个大小也小于 top 命令看到的,至此怀疑是直接内存泄漏了,只能用专门的分析工具去分析dump文件了。

注:堆外内存也和我们问题排查息息相关。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只IT攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值