一、概述
实际项目中,时常会有已经上线的问题出现问题,那么我们如何能够让项目能够健壮的运行呢?那么就需要我们进行诊断和分析,今天本文我们就来学习一下JVM常用命令和实战调优场景。
二、解析
1. 选择垃圾收集器的原则(吞吐量/反应时间)
我们先来介绍两个概念:
(1) 吞吐量 = 用户代码执行时间/(用户代码执行时间+垃圾收集执行时间)
(2) 反应时间快 = 用户线程停顿的时间短
我们可以针对不同的业务场景来选择锁使用的垃圾回收器的组合,如下:
(1) 单CPU或小内存,单机程序:-XX:+UseSerialGC
(2) 多CPU,需要最大吞吐量,如后台计算型应用:-XX:UseParallelGC或者-XX:UseParallelOldGC
(3) 多CPU,追求低停顿时间,需快速响应如互联网应用:-XX:+UseConcMarkSweepGC
2. 生产环境常用组合
具体组合的详细信息,如下图:
一般常用的就是PSPO,PN+CMS,G1这3种。
3. 常用参数设定
垃圾回收器的常用参数如下:
4. 何时触发YGC与FGC
触发YGC:
(1) 新生代的eden区空间不足
触发FGC:
(1) old空间不足
(2) 显示调用System.gc() ,包括RMI等的定时触发
(3) YGC时的悲观策略,它触发的机制是在首先会计算之前晋升的平均大小,也就是从新生代,通过ygc变成新生代的平均大小,然后如果老年代剩余的空间小于晋升大小,那么就会触发一次FullGC。sdk考虑的策略是, 从平均和长远的情况来看,下次晋升空间不够的可能性非常大, 与其等到那时候在fullGC 不如悲观的认为下次肯定会触发FullGC, 直接先执行一次FullGC。而且从实际使用过程中来看, 也达到了比较稳定的效果。
(4) dump live的内存信息时(jmap –dump:live)
5. CMS参数设定
CMS参数详情如下图:
6. G1参数设定
如下图:
7. GC日志分析
不同的垃圾回收器的GC日志格式是不一样的,我们先来分析一下PS的:
(1) 代码源码如下:
(2) 设置堆大小并且打印日志信息
(3) PS垃圾回收器GC日志详解如下:
Times:user用户态花费的时间;sys内核态花费的时间;real总共花费的时间
(4) 内存溢出后,会将整个堆的信息打印出来,如下图:
由上图可知,total = eden + from或者to,因为每次使用时,要么使用from,要么使用to。也就是两个survivor区因为copying算法,每次使用的时候只能使用到其中的一个。
8. JVM调优-预调优
(1) 熟悉业务场景,选择合适的垃圾回收器组合,响应时间和吞吐量做为选择依据。
(2) 预估机器配置,计算内存需求,需要多少G运行内存,CPU核数在预算范围内尽可能的多。
(3) 设定好年代分区比例和新生代转到老年代的年龄等。
(4) 设置GC日志打印文件,以便以后做问题追踪定位。设定日志参数,包含日志文件名、文件个数、每个文件大小等,具体参数如下:
(5)案例分析:
a. 垂直电商每日百万订单,订单系统需要什么样的服务器配置?
可以先做统计,看这一天哪个时间段订单并发量最高,可以根据最高并发数量,根据每次产生订单诞生的对象占据多大内存计算出总共需要的CPU内存。如果对响应时间有限制,那么就需要用压力测试来测出一个合适的配置。
b. 12306遭遇春节大规模抢票该如何支撑?
简而言之就是分流:CDN>LVS>NGINX>业务系统。部署多个服务器,各个区域的请求访问各自区域的服务器,并且库存也分散存储在不同的服务器上,由一个服务器做统一调配,这是为了避免出现数据倾斜问题,一台服务器上的票很快卖完了,另一台库存却还很充足。一种可能的模型是:下单>减库存和订单(redis、kafka)同时异步进行>等待付款。
9. JVM调优-优化环境
(1) 场景一:有一个50万PV的资料类网站(从磁盘读取文档到内存),原服务器32位,1.5G的堆,用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了。
a. 为什么原网站比较慢?
很多用户浏览数据,很多数据load到内存,内存不足,频繁GC,STW时间长,响应时间变慢。
b. 为什么新服务器更卡顿?
内存越大,FGC时间越长。
c. 如何进行调优
PS+PO换成PN+CMS或者G1
(2) 场景二:系统CPU经常100%,如何调优?
CPU经常100%表示一定有线程在占用系统资源:
a. 找出哪个进程CPU高 (top)
b. 该进程中哪个线程CPU高 (top -Hp)
c. 导出该线程的堆栈(jstack)
d. 查找出哪个方法(栈帧)消耗最高(jstack)
(3) 场景三:系统内存飙高,如何查找问题?
a. 导出堆内存 (jmap)
b. 分析(jhat jvisualvm jprofiler)
10. jdk自带的常用工具
(1) top命令
观察哪个进程CPU占用率居高不下,如下图:
我们再利用top -Hp 1502获取这个进程下所有线程的信息,如下图:
我们可以看到编号为1519的线程占比CPU最高。
(2) jps命令
列出所有的java进程。如下图:
(3) jstack命令
jstack 25308会打印出所有线程的相信信息,如下图:
根据所有线程的详细信息,我们就可以看到这些线程的状态,重点需要关注WATING、BLOCKED。
由于线程很多,在排查问题时就不好定位。所以阿里巴巴开发者手册上规定,线程的名称(尤其是线程池)都要写有意义的名称。
举例:waiting on <0x0000000088ca3310> (a java.lang.Object) 就表示在等待获取一个java.lang.Object的琐。假如有很多进程都在waiting on <xx>,那么需要我们搜索stack dump的信息,找到哪个线程持有这把锁处于RUNNABLE。
(4) jinfo pid
显示指定线程的详细信息,如下图:
(5) jstat -gc
打印指定线程的GC信息,如下:
动态观察GC情况。jstat -gc 4655 500:每500毫秒打印一次GC的情况。
但是这种方式所打印出的信息还是不够直观的,我们也可以利用jconsole.exe来连接进程来观察这个进程的相关信息,如下图:
jconsole.exe的工具图形界面不够美观,我们也可以使用jvisualvm.exe来连接进程观察详细信息,如下图:
图形界面一般不直接在线上环境上使用,会影响系统运行。在线跟踪还是推荐 arthas 。图形界面一般用在测试阶段,对系统进行压力测试、监控问题排查等。
(6) jmap -histo 4655 | head -20
显示进程4655的前17个数量对多的对象(有3行显示别的内容),如下图:
显示了有哪些类哪些对象占用了多少个字节等。
如果是线上系统排查问题,内存特别大的时候,jmap执行期间会对进程产生很大的影响,甚至卡顿。所以我们处理方式有以下几种:
a. 设定参数HeapDump,OOM的时候会自动产生堆转储文件。
b. 做高可用,有很多服务器,停掉一台对其他服务器不影响,也不影响系统使用。
c. arthas 在线定位问题。
三、总结
本文主要是介绍了一些参数来进行GC日志的打印阅读,介绍了JVM调优的基础概念以及一些小的调优场景,下一篇我们将针对各种实战场景了解更多的JVM调优所使用到的工具等。
更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!