系统上线后,最简单的要关注的性能指标:
机器级别的:cpuloadmemory

jvm级别的:进程、内存 、GC 

一、先说机器级别的,常用的命令有:uptime /top /ps

1、【Uptime】:系统开机运转到现在经过的时间、连线的使用者数量、最近一分钟,五分钟和十五分钟的系统负载。

范例:如下图

说明:

16 days20:34 主机已运行时间

3 users:用户连接数

load average:系统平均负载,系统平均负载被定义为在特定时间间隔内运行队列中的平均进程数,一般来说只要每个CPU的当前活动进程数不大于cpu核数,那么性能就是良好的。 

2、【Top: 能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器

范例:如下图

说明:

统计信息区:

第一行是任务队列信息,同 uptime 命令的执行结果。

第二行Tasks: 进程总数, running: 正在运行的进程数,sleeping: 睡眠的进程数,stopped:停止的进程数,zombie: 僵尸进程数。(比较需要注意的是最后的 zombie 那个数值,如果不是0,就要好好看看是哪个进程)。

第三行Cpu(s):  us:户空间占用CPU百分比, sy:内核空间占用CPU百分比,ni:用户进程空间内改变过优先级的进程占用CPU百分比, id:空闲CPU百分比,wa:等待输入输出的CPU时间百分比, hisist暂时还不太清楚。(CPU使用率:不是很好评估,一般只要不是持续保持超过50%都是正常的)。

最后两行为内存信息,内容如下:
Mem:  total:
物理内存总量,used:使用的物理内存总量,free:空闲内存总量,buffers:用作内核缓存的内存量。
Swap:  total:
交换区总量,used:使用的交换区总量,free:空闲交换区总量,cached:用于高速缓存的内存的大小。(内存一般只要swap使用率不高就是ok)。 

进程信息区:

PIDProcess ID):进程标示号 ( 每个 process  ID )

USER:进程所有者的用户名 ( process 所属的使用者 )

PR:进程的优先级别 ( Priority 的简写,程序的优先执行顺序,越小就越早被执行 )

NI:进程的优先级别数值 ( Nice 的简写,与 Priority 有关,也是越小就越早被执行 )

VIRT:进程占用的虚拟内存值。

RES:进程占用的物理内存值。

SHR:进程使用的共享内存值。

S:进程的状态,

D=不可中断的睡眠状态
R=
运行
S=
睡眠
T=
跟踪/停止
Z=
僵尸进程

%CPU:该进程占用的CPU使用率。

%MEM:该进程占用的物理内存和总内存的百分比。

TIME+:该进程启动后占用的总的CPU时间 ( CPU 使用时间的累加 )

Command:进程启动的启动命令名称。 

3ps】:该命令显示瞬间进程的状态,并动态变化

ps au  显示较详细的资讯

ps aux 显示所有包含其他使用者的行程

范例:如下图

说明:

au(x) 输出格式 :

USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

 

USER: 行程拥有者

PID: pid

%CPU: 占用的 CPU 使用率

%MEM: 占用的记忆体使用率

VSZ: 占用的虚拟记忆体大小

RSS: 占用的记忆体大小

TTY: 终端的次要装置号码 (minor device number of tty)

STAT: 该行程的状态

不可中断 uninterruptible sleep (usually IO)

运行 runnable (on run queue)

睡眠 sleeping

停止 traced or stopped

僵死 a defunct (“zombie”) process

START:行程开始时间

TIME: 执行的时间

COMMAND:所执行的指令

最常用的方法是ps aux,然后再利用一个管道符号导向到grep去查找特定的进程,如 :ps aux|grep java

 

二、再说jvm级别的,常用的命令有:jstackjmapjstat

比如我们在使用上述pstop 命令时发现哪个进程占用的cpu使用率较高,就可以单独针对这个进程使用jstackjmap命令。

注意:jstackjmap 不建议在线上环境随便使用,一般先要dump,再用工具分析。

使用命令:sudo -u admin  jmap -dump:format=b,file=/tmp/tmp.hprof  pid

然后再使用工具 mat 进行分析。

1、【jstack

范例:sudo –u admin jstack 28820 ,不要在线上随便使用。

说明:内容好多,暂时还没看懂。(具体的用法还要再看下,这次先不分享)

2、【jmap

范例1sudo -u admin jmap –heap 28820


范例2sudo -u admin jmap -histo:live 28820(显示进程28820中存活的对象)

Jmap参数如下:
-heap
 : 打印jvm heap的情况
-histo
 打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。
-histo
live  同上,但是只答应存活对象的情况
-permstat
 打印permanent generation heap情况 

3、【jstat】查看jstat输出日志

范例:sudo -u admin /opt/taobao/java/bin/jstat -gcutil 10053 1000  (10053 pid 1000 是每隔1000ms 打印一次)

注意:黄底部分只能写全路径。另外由于只能查看自己用户的进程,所以要加 sudo

说明:

YGC — 从应用程序启动到采样时发生 Young GC 的次数

YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)

FGC — 从应用程序启动到采样时发生 Full GC 的次数

FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)

GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC(应用程序启动时间可以用ps命令查看到(START))

每次young gc消耗的时间,可以用相间隔的两行YGCT相减得到。每次full gc消耗的时间,可以用相隔的两行FGCT相减得到。

 

一般jvm 1h会有一次fgc。而服务器也确实是运行了大概2天的时间,所以上图中fgc的数量保持在48不变,说明没有什么问题

 

JVM 栈(stack):存放局部变量(基本数据类型, 对象引用)。

JVM堆(Heap新生代(Young + 旧生代(Tenured):存放对象实例。

新生代(Young= Eden + Survivor区 ,Eden区为对象通常最初分配到的地方,Survivor区分为S0S1两块大小相等的区域,新生代(Young= E + S0 + S1

JVM  = E + S0 + S1 + O

绝大多数的对象都在young generation被分配,也在young generation被收回,当young generation的空间被填满,GC会进行minor collection,即新生代的GC,也叫young GC

新生代中未被回收的对象被转移到旧生代,然而旧生代也会被填满,满到无法放下从survivor熬出来的对象,那么,YGC就不会再次触发,而是会使用fullGC对整个堆进行GC

方法区:也被成为持久代,用来存放JVM加载的类型信息,方法区是全局共享的,他有自己的GC机制,跟我们说的YGCFGC不是一回事。

-Xms                        -- 设置堆内存初始大小

-Xmx                        -- 设置堆内存最大值

-XX:MaxTenuringThreshold    -- 设置对象在新生代中存活的次数

-XX:PretenureSizeThreshold  -- 设置超过指定大小的大对象直接分配在旧生代中

-Xmn                        -- 设置新生代内存大小。当新生代设置得太小时,也可能引发大对象直接分配到旧生代中。

-XX:SurvivorRatio           -- 设置EdenSurvivor空间的大小比例

-XX:PermSize                -- 设置Perm区的初始大小

-XX:MaxPermSize             --设置Perm区的最大值 

4、查看fgc

查看/home/admin/logs/gc.log

有这种信息,说明发生了fgc。 

-------------------------------------------------------------------------------

Supply问题过程还原:

周五(2012-07-06 14:17supply发布完毕

半小时后打开线上采购单列表页面,发现打不开列表页面,后台查看线上日志,报调用rmhsf接口超时。后面绑定了预发环境进行测试,预发环境下打开采购单列表大约1.8s左右。由于供应商急于出货,故代码进行了回滚。未能在线上查明原因,初步怀疑rm接口存在性能隐患。后在rm主干中加入日志,并在supply的主干代码中编写了多线程调用rm的接口,并对supply进行了预发,发现rm其实并未报错。故初步怀疑是没有发布好。根本原因还是没有找到。 

周一又进行了一次发布,时间:2012-07-09 10:48,上线后发现采购单列表仍然打不开,但依赖了rm的应用品牌特卖的elink和服务的tmallservice都能正常访问,确定了不是rm的问题,那么问题只能在suppply本身了。于是联系PE将日志打包发给开发,对日志进行分析。开发同学发现所有的hsf服务都调用超时。下午由于客户要使用系统,故又进行了回滚。分析日志没有实质性进展,提交了性能bug给性能团队。 

在耿电和韩木、毕玄的协助下最后定位到了问题。 

在这里还要非常感谢韩木同学的支持,搭建了性能测试环境,在性能环境体验如下:

目前性能服务器上还是老的代码以及错误的配置:

当时的jboss的配置如下:

说明:

堆内存初始大小:1536m

堆内存最大值:1536m

新生代内存大小:700m

Perm区的初始大小:96m

Perm区的最大值:96m

EdenSurvivor空间的大小比例:8:1 

1、启动jboss,在/home/admin/supply/bin目录下运行命令:

sudo -u admin -H ./jbossctl restart

2、过大概20-30min,运行pstop命令,情况如下:

运行 top

 

运行 ps -ef|grep java 可以看到JVM的运行参数

3、初步定位到进程11332有问题,然后看看gc的情况:

运行 sudo -u admin /opt/taobao/java/bin/jstat -gcutil 11332 1000

可以看到 FGC的次数在不断增加,说明发生了FGC

 

4、我们需要使用工具分析下为什么会发生fgc

运行sudo –u admin  jmap -dump:format=b,file=/tmp/tmp.hprof  11332

使用工具mat进行分析,下载地址:http://www.eclipse.org/mat/

将性能服务器上dump出来的文件下载到本地,然后用mat打开。heap dump是特定时间点,java进程的内存快照。有不同的格式来存储这些数据,总的来说包含了快照被触发时java对象和类在heap中的情况。饼图显示了各种对象占用内存大小的情况。


除了查看饼图,一般还需要进一步重点查看的是这两个信息 

(1)Histogram图显示了进程中的变量类型占用内存的情况:


 

Shallow size就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。在32位系统上,对象头占用8字节,不管成员变量(对象或数组)是否引用了其他对象(实例)或者赋值为null它始终占用4字节。Retained size是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。

这个图跟直接执行jmap -histo同样的效果,但是jmap -histo的统计方式不太一样,通常给不了太大的帮助,所以都得通过Dominator Tree来判断到底是什么原因。 

(2)Dominator Tree列出了进程中大的对象的内存占用情况


点开+号,可以看到对象中各成员占用的内存大小。

基本上这个图就可以说明问题了。也不一定内存占用百分之多少就有问题,还要结合看gc的频率,如果fgc很频繁,且基本回收不了,多数情况是内存泄露,或heap开的太小。