文章目标

当Java项目出现性能瓶颈的时候,通常先是对资源消耗做分析,包括CPU,文件IO,网络IO,内存;之后再结合相应工具查找消耗主体的程序代码。本文主要介绍系统资源消耗的分析过程,以及常用的Java线程分析方法。

CPU分析

在Linux中,CPU主要用于处理中断、内核及用户任务,优先级为:中断>内核>用户。在分析CPU消耗状况的时候,需要了解以下三个概念。

上下文切换

每个CPU(或多核CPU的每个核心)在同一时间只能执行一个线程<不包括超线程CPU>,Linux采用抢占式调度。当线程执行到达一个时间片后,如果线程有IO阻塞或高优先级线程要执行的时候,Linux将执行线程切换,切换前先保存当前线程执行状态(现场),并恢复待执行线程状态,这个过程就叫做上下文切换。在Java应用中,文件IO、网络IO、锁等待、线程Sleep操作都会使该线程进行阻塞或睡眠状态,从而触发上下文切换。频繁的上下文切换会造成内核占用较高的CPU,使得响应速度下降。

运行队列

每个CPU核心都维护了一个可运行队列,例如一个4核CPU,启动8个线程,且8个线程都处于可运行状态,平均分配情况下,每个核心的可运行队列里就有2个线程。通常而言,系统的load是由CPU运行队列决定的,假设以上状态维持了1分钟,则1分钟内系统load就是2。运行队列值越大,代表线程要消耗越长的时间才能执行完成。通常建议每个核心运行队列为1-3个。

利用率

CPU利用率指在用户进程,内核,中断处理,IO等待以及空闲五个部分百分比,这五个值是用来分析CPU消耗情况的关键指标。Linux System and NetWork Performent Monitoring建议用户进程/内核消耗比例为 65%-70% / 30%-35% 左右。

常用top, pidstat, sar, vmstat 1 分析占用情况,下图是top示例

us:用户进程处理占用百分比

sy:内核线程处理占用百分比

ni:被nice命令改变优先级的任务所占百分比

id:cpu空闲时间占用百分比

wa:在执行过程中等待IO所占百分比

hi:硬件中断占用百分比

si:软件中断占用百分比

st:虚拟机偷取时间百分比

对Java应用而言,线程消耗主要体现在us, sy上:

us: 用户进程处理占用百分比

us占用分析,需要依靠相关命令找出主体消耗线程ID(tid),然后转化成十六进制(printf "%x\n" tid),再用 kill -3 java_pid或 jstack -l java_pid 命令dump出线程信息,通过之前的十六进制值在dump信息中找到nid相等的线程,即为消耗CPU的线程。采样的时候要多做几次,保证找到的是真实的消耗线程。

在Java应用中如果us占用过高,代表运行的应用程序消耗了大部分CPU,常见为线程一直处理可运行状态(Runnable),并且无阻塞地执行循环,正则或复杂计算;也可能是每次请求都分配大量内存,导致频繁GC甚至频繁FullGC造成的,这时就需要依靠jvm工具查看了(jps, jmap, jstat等) 。

sy: 内核线程处理占用百分比

sy值过高表示Linux花费大量时间在线程切换上,Java造成原因通常是启动大量线程,且多数线程处理不断阻塞(如IO等待,锁等待)和执行的状态变化中,造成大量上下文切换。这时可通过 kill -3 java_pid或jstack -l java_pid 命令dump出线程信息,找出不断切换线程执行状态的原因(也可以通过TDA分析)。

如下使用 vmstat 1 查看上下文切换(cs)及sy占用

如果cs值很高的话,再使用 jstack -l java_pid 查看线程堆栈信息,通常可以发现大量线程处于TIMED_WAITING (on object monitor)与Runnable状态转化中,通过on object monitor可以找到锁竞争激烈的代码,从而找出上下文切换的原因。

文件IO分析

Linux在操作文件的时候,会将文件放入文件缓存区,直到内存不够或系统要释放内存给用户进程使用时,才会交换出去。因此在查看内存状态时经常发现可用(free)的物理内存不足,但cached用了很多,这是Linux提升文件IO速度的一种方法。这种情况下,如果物理内存足够用,真正的文件IO只有写文件和第一次读的时候才会产生。

在Linux中文件IO主要通过 pidstat, iostat分析:

pidstat -d -p java_pid 1 3

KB_rd/s 表示每秒读取的KB数, KB_wr/s表示每秒写入的KB数, 还可以加入-t参数显示具体的线程信息。

iostat

iostat只能看到整个系统的文件IO,不能查看具体进程消耗情况。Device表示设备卷标名或分区名,tps是每秒的IO请求,是IO消耗关键指标;Blk_read/s表示每秒读的块数量,Blk_wrtn/s表示每秒写的块数量;Blk_read, Blk_wrtn表示总共读写的块数量;当%iowait占用很高的时候,就要关注IO消耗状况了,这时可以使用 iostat -x 观察:

r/s, w/s 表示每秒读写的请求数, await表示平均每次IO操作的等待时间,avgqu-sz表示等待请求的队列的平均长度,svctm表示平均每次设备执行IO操作的时间,util表示一秒之中有百分之几用于IO操作。

在Java应用中造成文件IO消耗严重的原因,通常是多个线程进行大量写入操作(如频繁写入日志文件)。这时可以通过pidstat或iostat结合jstack线程信息,找到消耗主体程序。

网络IO分析

在分布式Java应用中,网络IO的消耗是非常值得关注的,尤其注意网卡中断是不是均匀地分配到各CPU上(cat /proc/interrupts)。Linux使用sar分析网络IO消耗情况:

sar -n ALL 1 2

主要观注接包(rxpck/s),发包(txpck/s),接包失败(rxerr/s),发包失败(txerr/s),丢包(rxdrop/s),Socket信息(tcpsck , udpsck)。

由于无法观察具体进程的网络IO消耗,在网络IO消耗高时,只能线程dump,通常这些线程都在进行网络读写操作。在Java网络通信中,通常将对象序列化为字节流发送,反序列化生成对象。

内存分析

从Java应用角度上看,内存可分为两部分,即JVM内存与非JVM内存。在JVM中内存消耗主要体现在堆内存上,内存消耗过高会导致频繁GC甚至FullGC,CPU占用高,可以通过jmap, jstat, mat, visualvm等工具跟踪内存消耗情况;生产环境下,通常将 -Xms 和 -Xmx调整为相同的值,避免运行时不断申请内存。非JVM内存通常只有在创建线程或使用DirectByteBuffer时才会产生,最值得关注的是swap的消耗与物理内存的消耗。

vmstat

swpd表示虚拟内存已使用的部分(kb),free空闲物理内存,buff表示用于缓冲的内存,cache表示用于作为缓存的内存。swap下的si表示每秒从disk读到内存的数据量,so每秒从内存写入disk的数据量。swpd过高表示物理内存不够用,系统需要频繁从虚拟内存与disk交换数据,严重影响系统的性能。

sar -r 2 5

通过sar工具可以看到内存占用,空闲,buff, cache的情况。当物理内存空闲时,Linux会使用一部分内存用于buffer以及cache,以提高系统运行效率。因此可认为系统可用物理内存为 kbmemfree + kbbuffers + kbcached。

此外还可以使用top, pidstat -r -p [pid][interval][times]

pidstat -r -p 2448 1 5

参考资料

中断:http://blog.csdn.net/pxz_002/article/details/7327668

CPU占用分析:http://www.cnblogs.com/yjf512/p/3383915.html

林昊:分布式Java应用

JVM内存分析:http://my.oschina.net/feichexia/blog/196575

https://wenku.baidu.com/view/c7c38dbe4b35eefdc8d333a8.html

Java项目性能瓶颈定位的更多相关文章

  1. Java项目性能瓶颈分析及定位(八)——Java线程堆栈分析(五)

    对于CPU而言,常见的瓶颈主要有两种:服务器的压力很小,但是CPU的利用率却很高,这样的性能瓶颈相对比较容易定位(好比我只是说了你一句,你就哭了,你的弱点立马就暴露出来了):给服务器施加的压力很大,但 ...

  2. 使用maven来管理您的java项目

    maven是一个项目管理工具,使用maven可以自动管理java项目的整个生命周期,包括编译.构建.测试.发布和报告等.在大型项目开发中,使用maven来管理是必不可少的. 一.安装maven 1.W ...

  3. java项目获取根路径(web项目和application项目的区分)

    Java项目中经常要读取配置文件,涉及到读取配置文件的地方,就会要读定位文件的路径.因此,在项目如何正确获取文件路径尤为关键. 根据不同的java项目,在获取文件路径时候有一些 小区别 测试环境:E: ...

  4. linux Java项目CPU内存占用高故障排查

    linux Java项目CPU内存占用高故障排查 top -Hp 进程号 显示进程中每个线程信息,配合jstack定位java线程运行情况 # 线程详情 jstack 线程PID # 查看堆内存中的对 ...

  5. Java项目中使用log记录日志的一些总结

    本文介绍了一下自己在Java项目中使用log的一些总结,从日志的作用.日志的选用.日志级别介绍.日志记录的一些最佳实践几个方面阐述. 日志的作用 主要作用包括: 1.出问题后定位当时问题 2.显示程序 ...

  6. linux中Java项目占用cpu、内存过高时的排查经历

    一.使用top命令查看占用高资源的java项目的进程ID(pid): top 二.查看该进程中的线程所占用资源的情况:top -Hp pid 三.查看该线程对应的16进制:printf %x 1112 ...

  7. docker swarm实现java项目的发布/滚动更新/回滚/镜像管理

    使用docker swarm滚动更新java项目,部署集群,这一切的前提是使用Jenkins+maven进行项目打包,分发等功能 具体可以参考我的另外三篇文章 https://www.cnblogs. ...

  8. 利用内存分析工具(Memory Analyzer Tool,MAT)分析java项目内存泄露

    转载:http://blog.csdn.net/wanghuiqi2008/article/details/50724676 一.开发环境: 操作系统:ubuntu 14.04 IDE:Eclipse ...

  9. Ubuntu环境下使用Maven编译并打包Java项目

    一.安装Maven 打开终端输入以下指令: $ mvn -v Apache Maven Maven home: /usr/share/maven Java version: 1.8.0_181, ve ...

随机推荐

  1. Qt 学习之路 2(7):MainWindow 简介

    Qt 学习之路 2(7):MainWindow 简介  豆子  2012年8月29日  Qt 学习之路 2  29条评论 前面一篇大致介绍了 Qt 各个模块的相关内容,目的是对 Qt 框架有一个高屋建 ...

  2. Patting Heads

    Description It's Bessie's birthday and time for party games! Bessie has instructed the N (1 < N & ...

  3. java重载equals和hashCode

    class Employee { private int salary; private java.util.Date hireDay; private String name; public int ...

  4. Java String 字符串操作小结

    // 转载加编辑 -- 21 Apr 2014 1. Java字符串中子串的查找 Java中字符串中子串的查找共有四种方法,如下: 1.int indexOf(String str) :返回第一次出现 ...

  5. 杭电ACM hdu 2079 选课时间 (模板)

    Problem Description 又到了选课的时间了,xhd看着选课表发呆,为了想让下一学期好过点,他想知道学n个学分共有多少组合.你来帮帮他吧.(xhd认为一样学分的课没区别) Input输入 ...

  6. Java——flush()方法

    Java在使用流时,缓冲区是一种发送数据的高效方法,但当溢出缓冲区的部分需要用flush()方法强制将数据发送出去,不必等到缓冲区再次装满,尤其是在数据量特别小的情况下,如果不使用此方法,很容易出现流 ...

  7. js 获取非行间样式

    1.getComputedStyle(nodeObj,false):该方法是BOM对象,第一个是要获取样式的节点对象:第二个可以写成任何的字符一般写成false或者null,这里最好是用false因为 ...

  8. @RequestParam详解以及加与不加的区别

    以前写controller层的时候都是默认带上 @RequestParam 的, 今天发现不加@RequestParam 也能接收到参数 下面我们来区分一下加与不加的区别 这里有两种写法 @Reque ...

  9. Python镜像源

    镜像源 官方:https://pypi.python.org/pypi 豆瓣:https://pypi.doubanio.com/simple/ 阿里:http://mirrors.aliyun.co ...

  10. Android官方架构组件介绍之ViewModel(三)

    ViewModel 像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的.Framework可能会根据用户的一些操作和设备的状态对Act ...