JVM——性能调优的突破方向与解决策略

寻找性能瓶颈

CPU消耗分析

基本概念

上下文切换:切换执行线程时,存储目前线程的执行状态,恢复要执行的线程的状态

运行队列:每个CPU核将要执行的线程

利用率:CPU在用户进程、内核、中断处理、IO等待、空闲,五个部分使用百分比

指令

1:top:查看CPU的消耗情况,按1按照核来显示消耗情况,按shift+h按线程查看CPU消耗情况

us:用户进程 sy:内核线程 ni:nice命令改变优先级 id:空闲时间 wa:等待io

hi:硬件中断 si:软件中断

2:pidstat:输入pidstat 1 2,在console上每隔1秒输出目前活动进程的CPU消耗情况,共输出2次

常见问题

1:us:

首先找到具体消耗CPU的线程所执行的代码

1:找到消耗CPU严重的线程及其ID,将ID转化为十六进制

2:通过kill -3 [javapid]或jstack的方式dump出应用的java线程信息(jstack [pid] | grep 'nid=0x6849')

3:通过ID找到对应的nid值的线程

原因:

1:线程一直属于可运行(Runnable)状态

2:频繁的GC


2:sy:

sy值高,表示Linux花费了更多的时间在进行线程切换

原因:启动的线程过多,且线程多数处于不断阻塞和执行状态的变化过程中,产生大量上下文切换

解决:通过kill -3 [javapid]或jstack -1 [javapid]方式dump出Java应用程序的线程信息,找出等待状态或锁竞争过多的线程

文件IO消耗分析

跟踪线程文件IO消耗的方法

1:pidstat:pidstat -d -t -p [pid] 1 100 类似的命令查看线程的IO消耗状况

2:iostat:查看各个设备的IO历史状况,可以输入iostat -x xvda 3 5定时采样查看IO的消耗状况

此时首要关注的是CPU中的iowait%所占的百分比

当IO消耗过高时

1:通过pidstat直接找到文件IO操作多的线程

2:结合jstack找到对应的Java代码

原因

多个线程需要大量内容写入的动作,或磁盘设备本身处理速度慢,文件系统慢,操作的文件本身很大造成的

网络IO消耗分析

采用sar分析网络IO的消耗状况

信息有三部分

第一部分为网卡成功接包和发包的信息

第二部分为网课失败接包和发包的信息

第三部分为sockets上的统计信息

对java而言,主要使用tcpsck和udpsck

当网络IO消耗高时,对线程dump,查找产生大量网络IO操作的线程

Java实现网络通信时,要将对象序列化为字节流进行发送,或读取字节流进行反序列化为对象,这个过程要消耗JVM堆内存

堆内存大小通常有限,所以一般不会造成网络IO消耗严重

内存消耗分析

JVM堆以外的内存方面的消耗

关注swap的消耗以及物理内存的消耗

查看内存消耗的方式:

1:vmstat:关注memory下的swpd、free、buff、cache以及swap下的si、so

swpd:虚拟内存已使用的部分,过高就是物理内存不够用

free:空闲的物理内存

buff:用于缓冲的内存

cache:作为缓存的内存

si:每秒从disk读至内存的数据量

so:每秒从内存中写入disk的数据量

2:sar:

通过sar的-r参数可以查看内存的消耗状况

kbswpfree:表示swap空闲的大小

kbswpused:表示已使用的swap大小

%swpused:表示使用的swap空间比率

系统中可用的物理内存为:kbmemfree+kbuffers+kbcached

sar相比vmstat的好处是可用查询历史状况,更加准确分析趋势状况

共同弱点是不能分析进程所占用的内存量

3:top:可用查看进程所消耗的内存量,不管看到的进程的消耗内存是包括JVM已分配的内存加上加上Java应用所耗费的JVM以外的物理内存

4:pidstat:可用查看进程消耗的内存量,命令格式为pidstat -r -p [pid] [interval] [times]

最佳的分析组合是结合top或pidstat,以及JVM的内存分析工具来共同分析内存的消耗状况

内存消耗过多一般解决思路:

1:分析耗费的是JVM外的物理内存还是JVM的堆

2:如果是物理内存,分析线程数量以及Direct ByteBuffer使用情况

3:如果是堆,结合分析工具来分析程序中具体对象的内存占用情况

资源消耗少,程序执行慢

1:锁竞争激烈:多数线程处于等待状态

2:未充分使用硬件资源:双核CPU,但程序都是单线程串行操作

3:数据量增长:数据库读写速度下降

调优

四个方面:硬件、操作系统、JVM、程序

JVM调优

代大小的调优

前缀知识:

-Xmx:设置JVM最大可用内存

-Xms:设置JVM初始内存

-Xmn:设置年轻代大小

-XX:NewRatio=a:设置年轻代与老年代的比值,老年代占比为a

-XX:SurvivorRatio=b:设置Survivor区与Eden区的比值,Eden区占比为b

-XX:MaxPermSize=c:设置持久代大小为c

-XX:MaxTenuringThreshold=d:设置垃圾最大年龄为d


1:避免新生代设置过小

一、minor GC次数频繁

二、minor GC对象直接进入老年代,占据老年代剩余空间,触发Full GC

2:避免新生代设置过大

一、老年代变小,导致Full GC频繁

二、minor GC耗时增加

3:避免Survivor区过小或过大

Survivor区大的话,可以存储更多在minor GC后仍存活的对象 避免其进入旧生代

但是Eden区变小,minor GC触发次数增加

Eden区大的话,minor GC触发次数降低,但是Survivor区变小,有超过Survivor区大小的对象在minor GC后没有被回收的话,就会直接进入老年代

4:合理设置新生代存活周期

增大存活周期后,对象在Minor GC阶段被回收的机会就增加了,但同时带来的是survivor区被占用

控制代大小指令的总结:

1:内存不够用时可以使用-Xms、-Xmx调整整个JVM堆的大小,能多大取决于操作系统和CPU

2:-Xmn让新生代变大,使得多数对象在minor GC阶段被回收,但意味着更频繁的Full GC

3:-XX:SurvivorRatio让Eden区变大,降低minor GC频率,但对没被会受到对象容易直接进入老年代。让Survivor区变大可以避免对象频繁直接进入老年代。

GC策略调优

看书p200页的案例:

213秒内发生了一次Full GC,不正常,根据分析发现大部分老年代对象可以回收,说明他们只要在新生代多活一段时间,就可以被minor GC回收,所以,扩大survivor区,最好是直接扩大堆内存。

第二种方法是GC策略调整为CMS GC,可以在发生Full GC前回收大部分新生代转入老年代的对象

总结

如果不是有确切的GC造成性能低的理由,就没必要做过多细节方面的调优,多数情况下只需选择GC策略并设置JVM堆大小即可

程序调优

1:CPU us高

原因:执行线程无任何挂起动作

优化方法:对这种线程的动作增加Thread.sleep,以损失单次执行性能为代价

还有一种情况是状态的扫描,等其他线程改变值后才能继续执行,这时采用wait/notify机制

2:CPU sy高

原因:线程的运行状态经常切换

优化方法:减少线程数

如果应用要支撑大量的并发,在减少线程数的情况下最好增加一个缓冲队列,避免线程数的减少造成系统出错率上升

原因二:线程之间锁竞争激烈

优化方法:降低锁竞争,如果线程数过多,调优后可能造成us值过高

原因三:网络IO或确实需要锁竞争,需要支撑高并发

优化方法:协程,Kilim框架 ,在线程阻塞时,不浪费相对昂贵的原生线程资源

Kilim承担了线程的调度以及上下文切换动作,带来了线程使用率的提升,但是在JVM堆中保存Task上下文信息,消耗更多的内存


3:文件IO消耗严重

原因:多个线程在写大量数据到同一文件,文件变大,写入速度变慢,各线程争抢文件锁

调优方法:异步写,批量读写,限流,限制单个文件大小,采用缓冲区避免与操作系统的频繁交互

4:网络IO消耗严重

原因:需要发送或接收的包太多

调优方法:限制发送packet的频率


5:内存消耗严重

1.释放不必要的引用

2.使用对象缓存池

3.采用合理的缓存失效算法

如果放入太多对象在缓存池中,反而会造成内存的严重消耗,要合理控制缓存池的大小

4.合理使用软引用和弱引用


6:资源消耗少,程序执行慢

1:锁竞争激烈

1.使用并发包中的类减少竞争

2.尽可能少用锁

3.拆分锁

4.去除读写操作的互斥锁

2:未充分发挥硬件资源

未充分使用CPU:在能并行处理的场景中未使用足够的线程

未充分使用内存:在内存资源消耗可接受的范围内使用缓存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值