CPU的消耗
查看CPU的消耗,目前最直接的办法就是通过linux系统的top命令来查看。如上文所述。
对Java应用程序而言,CPU的消耗主要体现在us,sy两个值上,下面分析下这两个值:
1. us
当us值过高时,表示运行的应用程序消耗了大部分的CPU。在这种情况下,对Java程序而言,如何找到具体消耗CPU的线程所执行的代码呢?
首先通过linux提供的命令找到消耗CPU严重的线程及其ID,将此ID转换为16进制的值。之后通过kill -3 <pid> 或 jastack的方式dump出应用的Java线程信息。通过之前转化的十六进制的值找到对应nid值的线程,该线程就是消耗CPU的线程。
Java应用造成us高的主要原因是线程一直处于Runnable状态,通常是这些线程在执行无阻塞,循环,正则或纯粹的计算动作所照成的。另外一个可能会造成us高的原因是频繁的GC。
2. sy
当sy的值高的时候,表示Linux话费了更多的时间在进行线程切换,Java应用造成这种现象的主要原因是启动的线程比较多,而且这些线程多数处于不断的阻塞(如锁等待,IO等待)和执行状态的变化中,这就导致了操作系统要不断的切换执行的线程,产生了大量的上下文切换。
在这种情况下,对Java应用而言,最重要的就是要找出线程状态不断切换的原因。可以通过kill -3 <pid> 或 jastack的方式dump出应用的Java线程信息,查看线程的状态信息以及锁信息,找出等待状态或锁竞争过多的线程。
文件IO的消耗
Linux系统在操作文件是,将数据放入文件缓冲区,直到内存不够或者系统要释放内存给用户进程用。因此在查看Linux内存状况时(free -g)通常会发现可用的物理内存不多,但cached用了很多,这时Linux提升文件IO速度的一种方法。这样的做法下,如果物理空闲内存不够,通常在Linux上只有写文件和第一次读取文件时会产生真正的文件IO。
在Linux系统中,要跟踪线程的文件IO消耗,可以通过iostat命令来查看:
Linux 3.10.0-327.el7.x86_64 04/20/2018 _x86_64_ (24 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
27.53 0.00 7.81 0.02 0.00 64.65
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda 112.64 2788.77 14334.25 53812230641 276594109712
在以上几项指标中,其中Device表示设备的卷标名或分区名。
- tps是每秒的IO请求数,也是IO消耗情况中值得关注的数字。
在使用iostat查看IO的消耗情况时,首先要关注的是CPU中的iowait%所占的百分比,当iowait占了主要的百分比时,就表示要关注IO方面的消耗了。这是可以通过iostat -x这样的方式看箱子的查看具体的情况。比如:
Linux 3.10.0-327.el7.x86_64 04/20/2018 _x86_64_ (24 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
27.53 0.00 7.81 0.02 0.00 64.65
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.01 50.21 23.72 88.92 2788.83 14334.30 304.02 0.05 0.47 0.16 0.55 0.13 1.51
其中值得关注的主要有:
- r/s:表示每秒读的请求数。
- w/s:表示每秒写的请求数。
- await:表示每次IO操作的等待时间,单位为毫秒。
- avgqu-sz:表示等待请求的队列的平均长度。
- svctm:表示平均每次设备执行IO操作的时间。
- util:表示一秒之中有百分之几用于IO操作。
当文件IO消耗过高时,对于Java应用最重要的是找到造成文件IO消耗高的代码,比较简单的寻找方法为采用pidstat命令(使用方式可使用命令man pidstat来查看)来直接找到文件IO操作多的线程。之后结合jstack找到对应的Java代码。
Java应用造成文件IO消耗严重主要是多个线程需要进行大量的内容写入(如日志)的动作,或磁盘本身的处理速度比较慢,或文件系统慢,或文件本身已经很大。
内存消耗分析
堆内存分析可通过JVM提供的工具来做,这里主要关注JVM堆以外的内存消耗(创建线程或使用Direct ByteBuffer等)。对于JVM对外内存的消耗,可以基于linux提供的命令来查看:
1. vmstat
在Linux的console中输入vmstat,输出例子如下所示:‘
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
5 0 0 410076 1696 15959792 0 0 120 617 0 0 28 8 65 0 0
其中:
- swpd:指虚拟内存中已经使用的部分,单位为kb。
- free:表示空闲的物理内存。
- buff:表示用于缓冲的内存。
- cache:表示用于缓存的内存。
- si:每秒从disk读至内存的数据量。
- so:每秒从内存中写入Disk的数据量。
如果swpd的值过高,通常是由于物理内存不够用了,os将物理内存中的一部分数据转为放入硬盘上进行存储以便腾出足够的空间给当前的应用程序使用。如swap IO发生的比较频繁,那么就会严重影响系统的性能。
由于Java应用是单进程的应用,因此只要JVM内存设置的不是过大,是不会操作到swap区域的。物理内存消耗过高可能是由于JVM内存设置过大,创建的Java线程过多或者通过Direct ByteBuffer往物理内存中防止了过多的对象造成的。