您的位置:首页 > 编程语言 > Java开发

Java性能优化权威指南-第2章 操作系统性能监控

2016-05-22 17:34 330 查看
1、定义
2、CPU使用率2.1监控CPU使用率vmstat
mpstat
top3、CPU调度程序运行队列3.1监控CPU调度程序运行队列vmstat4、内存使用率4.1监控内存使用率vmstat4.2监控锁竞争
4.3隔离锁竞争
4.4监控抢占式上下文切换
4.5监控线程迁移5、网络I/O使用率nicstat5.1应用性能改进的考虑6、磁盘I/O使用率
7、其他命令行工具应用的性能极限是相关人员在服务等级协议(Service Level Agreement,提供服务的企业和客户之间就服务的品质、水准、性能等方面所达成的双方共同认可的协议或契约)中关注的重点。找到性能极限的关键在于知道监控哪些数据、监控软件栈的哪些部分以及使用哪些工具。本章将介绍需要监控的操作系统数据以及可用的操作系统性能监控工具,此外还会给出定位性能问题的一般性指导原则。本章将讲解那些需要重点监控的系统属性以及为何要监控他们。本章首先会给出性能监控、性能分析及性能调优这几个概念的定义,然后介绍操作系统中重点需要监控的统计数据、监控性能统计数据的命令行,此外还就何种性能统计值可能会指示问题根源,或性能分析的下一步该采取什么行动给出指导意见。


1、定义

改善性能涉及3种不同的活动:性能监控、性能分析以及性能调优。性能监控是一种以非侵入式方式收集或查看应用运行性能数据的活动。监控通常是指一种在生产、质量评估或者开发环境中实施的带有预防或主动性的活动。当应用的干系人报出性能问题却没有足以定位根本原因的线索时,首先会进行性能监控,随后是性能分析。
性能分析是一种以侵入式方式收集运行性能数据的活动,它会影响应用的吞吐量或响应性。性能分析是对性能监控或是对干系人所报问题的回应,关注的范围通常比性能监控更集中。性能分析很少在生产环境中进行,通常是在质量评估、测试或开发环境中,常常是性能监控之后的行动。
性能调优是一种为改善应用响应性或吞吐量而更改参数、源代码、或属性配置的活动。性能调优通常是在性能监控或性能分析之后进行。


2、CPU使用率

要使应用的性能或扩展性达到最高,就必须充分利用分配给它的CPU周期,不能有丝毫浪费。如何让多处理器、多核系统上运行的多线程应用有效利用CPU周期,是个让人头疼的问题。此外,特别值得注意的是,应用消耗很多CPU并不意味着性能或扩展性达到了最高。要想找出应用如何使用CPU周期,可以在操作系统上监控CPU使用率。大多数操作系统的CPU使用率分为用户态CPU使用率和系统态CPU使用率。用户态CPU使用率是指执行应用程序代码的时间占总CPU时间的百分比。系统态CPU使用率是指应用执行操作系统调用的时间占总CPU时间的百分比。系统CPU使用率高意味着共享资源有竞争或者I/O设备之间有大量的交互。既然原本用于执行操作系统内核调用的CPU周期也可以用来执行应用代码,所以理想情况下应用达到最高性能和扩展性时,它的系统太CPU使用率为0%。所以提高应用性能和扩展性的一个目标是尽可能降低系统态CPU使用率。对于计算密集型应用来说,不仅要监控用户态和系统太CPU使用率,还要进一步监控每时钟指令数(Instructions Per Clock,IPC)或每指令时钟周期(Cycles Per Instruction,CPI)等指标。这两个指标对于计算密集型应用来说很重要,因为现代操作系统自带的CPU使用率监控工具只能报告CPU使用率,而没有CPU执行指令占用CPU时钟周期的百分比。这意味着,即便CPU在等待内存中的数据,操作系统工具任然会报告CPU繁忙。这种情况通常称为停滞(Stall)。当CPU执行指令而所用的操作数据不在寄存器或者缓存中时,就会发生停滞。由于指令执行前必须等待数据从内存装入CPU寄存器,所以一旦发送停滞,就会浪费时钟周期。CPU停滞通常会等待(浪费)好几百个时钟周期。因此提高计算机密集型应用性能的策略就是减少停滞或者改善CPU高速缓存使用率,从而减少CPU在等待内存数据时浪费的时钟周期。这种类型的性能监控可能需要性能专家的协助。


2.1监控CPU使用率


vmstat

Linux的vmstat显示所有虚拟处理器的总CPU使用率。一般vmstat工具的使用是通过两个数字参数来完成的,第一个参数是采样的时间间隔数,单位是秒,第二个参数是采样的次数
cpu 的us sy分别表示用户态CPU使用率和系统态的CPU使用率,id是空闲率或CPU的可用来。us,sy的和应该等于100减去id。


mpstat

mpstat (Multiprocessor Statistics) 实时多cpu监控工具,以列表方式展示每个虚拟处理器的CPU使用率。用mpstat 监控每个虚拟处理器的CPU使用率,有助于发现应用中的一些线程比其他线程消耗更多的CPU周期,还是应用的所有线程基本平分CPU周期。如果是后者,意味着应用的扩展性比较好。如果没有设置mpstat的报告间隔,则输出自系统最近一次启动以来所有的mpstat数据的总和。如果设置了报告间隔,统计数据的第一行是系统自最近一次启动以来的数据总和。用法 展开原码


top

Linux top命令不仅包括CPU使用率也包括进程统计数据和内存使用率。下面的例子显示它的输出主要包括两个部分,上半部分是整个系统的统计信息,下半部分是进程的统计信息(默认按CPU使用率由高到低排序)。top - 21:43:11[当前系统时间] up 163 days[系统已经运行了16天], 5:38, 1 user[个用户当前登录], load average: 0.03, 0.04, 0.05[1min,5min,15min系统负载,即任务队列的平均长度]
Tasks: 149 total[总进程数], 1 running[正在运行的进程数], 144 sleeping[睡眠的进程数], 0 stopped[停止的进程数], 4
zombie[冻结进程数]
Cpu(s): 1.1%us[用户空间占用CPU百分比], 0.5%sy[内核空间占用CPU百分比], 0.0%ni[用户进程空间内改变过优先级的进程占用CPU百分比], 98.1%id[空闲CPU百分比],
0.0%wa[等待输入输出的CPU时间百分比], 0.0%hi, 0.0%si, 0.3%st
Mem: 3922472k total[物理内存总量], 3719904k used[使用的物理内存总量], 202568k free[空闲内存总量], 132544k buffers[用作内核缓存的内存量]
Swap: 4192924k total[交换区总量], 8644k used[使用的交换区总量], 4184280k free[空闲交换区总量], 266660k cached[缓冲的交换区总量]进程统计信息 展开原码


3、CPU调度程序运行队列

除CPU使用率之外,监控CPU调度程序运行队列对于分辨系统是否满负荷也有重要意义。运行队列中就是那些已准备好运行、正等待可用CPU的轻量级进程。如果准备运行的轻量级进程数超过系统所能处理的上限,运行队列就会很长。运行队列长表明系统负载可能已经饱和。系统运行队列长度等于虚拟处理器的个数时,用户不会明显感到性能下降。此处虚拟处理器的个数就是系统硬件线程的个数,也就是Java
API Runtime.availableProcessors()的返回值。当运行队列长度大于虚拟处理的4倍或更多时,系统的响应就会非常迟缓了。一般性的指导原则是:如果在很长一段时间里,运行队列的长度一直都超过虚拟处理器个数的1倍就需要关心了,只是暂时还不需要立即采取行动。如果在很长一段时间里,运行队列的长度达到虚拟处理器个数的3~4倍或更高,则需要立即引起注意或采取行动。解决运行队列长有两种方法。一种是增加CPU以分担负载或减少处理器的负载量,这种方法从根本上减少了每个虚拟处理器上的活动线程数,从而减少运行队列中的轻量级进程数。另一种方法是分析系统中运行的应用,改进CPU使用率。换句话说,研究可以减少应用运行所需CPU周期的方法,如减少垃圾收集的频度或采用完成同样任务但CPU指令更少的算法。性能专家在减少代码路径长度以及为了改进CPU指令选择性时,通常会考虑这种方法。Java程序员可以通过更有效的算法和数据结构来实现更好的性能。这是因为,虽然现在JIT编译器可以产生成熟优化的代码以改善应用性能,但Java程序员几乎无法操纵JIT编译器,所以应该关注算法和数据结构的效率。通过性能分析可以找出哪些算法和数据结构值得关注。


3.1监控CPU调度程序运行队列


vmstat

Linux上可以用vmstat命令监控运行队列长度。vmstat输出的第一列是运行队列长度,值是运行队列中轻量级进程的实际数量。


4、内存使用率

除了CPU使用率还需要监控系统内存相关的属性,例如页面调度或页面交换、加锁、线程迁移中的让步式和抢占式上下文切换。系统在进行页面交换或使用虚拟内存时,Java应用或JVM会表现出明显的性能问题。当应用运行所需的内存超过可用物理内存时,就会发生页面交换。为了应对这种可能出现的情况,通常要为系统配置swap空间。swap空间一般会在一个独立的磁盘分区上。当应用耗尽物理内存时,操作系统会将应用的一部分置换到磁盘上的swap空间,通常是应用中最少运行的部分,以免影响整个应用或者应用最忙的那部分。当访问应用中被置换出去的部分是,就必须将它从磁盘置换进内存,而这种置换活动会对应用的响应性和吞吐量造成很大影响。此外,JVM垃圾收集器在系统页面交换时的性能也很差,这是由于垃圾收集器为了回收不可达对象所占的空间,需要访问大量的内存。如果Java堆的一部分被置换出去,就不行先置换进内存以便垃圾收集器扫描存活对象,这会增加垃圾收集的持续时间。垃圾收集是一种Stop-The-World(时空停滞)操作,即停止所有正在运行的应用线程,如果此时系统正在进行页面交换,则会引起JVM长时间的挺度。如果发现垃圾收集时间变长,系统有可能正在进行页面交换,为了验证这一点,必须监控系统的页面交换。


4.1监控内存使用率


vmstat

Linux上可以用vmstat输出中的free列监控页面交换,也可以用其他方法如top命令或/proc/meninfo文件来监控。vmstat需要监控si和so,他们分别表示内存页面换入和换出的量。此外,free列线上可用的空闲内存。留意是否会同时出现空闲内存减少和页面调度频繁的情形,相比而言,实际的数量单位反而不那么重要。如果观察到上述统计数据的模式,说明系统可能在进行页面交换。


4.2监控锁竞争

有许多Java应用因为锁竞争的问题而无法扩展。优化之后的现代JVM已经改善了应用遇到锁竞争时的性能。例如Java5中,Java Hotspot VM以用户代码而不是直接依赖操作系统锁原语的方式实现了许多锁优化逻辑、Java同步方法及同步块。在Java 5之前的版本中,Hotspot VM 几乎将所有的锁逻辑都委托给操作系统锁原语,这使得操作系统工具在查看系统CPU使用率以及smtx(spin on mutex,互斥量上的自旋次数)的同时,可以很轻易的监控Java应用中的锁竞争。由于Java 5 Hotspot VM以用户代码实现了许多优化锁逻辑,使得用mpstat查看smtx和sys系统态CPU使用率的方法无法奏效了。简要介绍一下Java 5以及以上版本的Hotspot VM中所增加的锁优化机制。线程通过忙循环自旋(Tight Loop Spin)尝试获得锁,如果若干次忙循环自旋之后任然没有成功,则挂起该线程,等待被唤醒再次尝试获取该锁。挂起和唤醒线程会导致操作系统的让步式上下文切换。让步式上下文的切换耗费的时钟周期代价非常高,通常高达80000个时钟周期。自旋锁的另一种通俗的解释是:在物理机器有多个处理器的系统中,可以同时有两个线程并行,假定他们会同时获取某个共享数据的锁。由于多数应用只会在很短的时间内锁住共享数据,所以对这两个线程而言,后尝试获取锁的线程没有必要直接挂起,可以先执行忙循环自旋等待一会,然后再尝试获取该锁。对于锁占用时间短的应用来说,自旋锁改善性能的效果非常好,但对于占用时间长的锁来说,反而加重了性能损失。可以遵循以下的一般性准则,对于任何Java应用来说,如果让步式上下文切换占去它5%或更多的可用时钟周期,说明它可能遇到了锁竞争,即便只占到3%~5%也值得进一步调查。Linux上可以使用sysstat包中的pidstat监控锁竞争。pidstat -w 报告是的是所有处理器每秒而不是每个间隔的让步式上下文切换,输出结果中的cswch/s是让步式上下文切换。注意它统计的所有处理器的让步式上下文切换。让步式上下文切换所耗费的时钟周期可用以下方法估算:计算公式: (让步式上下文切换 / 虚拟处理器数目) * 80000 / 该处理器的时钟周期pidstat -w


4.3隔离锁竞争

在Java源代码中追查竞争锁历来都是难题,要想找到Java应用中的竞争锁,通常是定期转存储线程,查找那些可能在多个线程中因共享锁而被阻塞的线程。后面的章节将介绍


4.4监控抢占式上下文切换

让步式上下文切换时指执行线程主动释放CPU,抢占式上下文切换时指线程因为分配的时间片用尽而被迫放弃CPU或者被其他优先级更高的线程锁抢占。pidstat的输出结果中cswch/s是每秒的让步式上下文切换,nvccswch/s是抢占式上下文切换。


4.5监控线程迁移

我们发现,待运行线程在处理器之前的迁移也会导致性能的下降。大多数操作系统的CPU调度程序会将待运行线程分配给上次运行它的虚拟处理器。如果这个虚拟处理器忙,调度程序就会将待处理线程迁移到其他可用的虚拟处理器。线程迁移会对应用性能造成影响,这是因为新的虚拟处理器缓存中可能没有待运行线程所需的数据或状态信息。多核系统上运行Java应用可能会发生大量的线程迁移,减少迁移的策略是创建处理器组并将应用分配给这些处理器组。一般性准则是,如果横跨多核或虚拟处理器的Java应用每秒迁移超过500此将Java应用绑定在处理器组上就有好处。


5、网络I/O使用率

分布式Java应用的性能和扩展性受限于网络带宽或网络I/O的性能。举例来说,如果发送到系统网络接口硬件的消息量超过了它的处理能力,消息就会进入操作系统的缓冲区,这会导致应用延迟。此外网络上发生的其他状况也会导致延迟。


nicstat

Linux网络I/O监控工具:netstat或sysstat可以提供每秒发送和接收的包数,包括错误和冲突的包,但是不能提供网络使用率。nicstat源码可以从http://sourceforge.net/projects/nicstat/files/下载,使用前需要编译。命令格式如下:nicstat [-hnsz] [-i interface[,.....]] | [interval] [count]其中-h是显示帮助信息,-n仅显示非本地接口,-s显示概要信息,-z跳过0值,-i interface是网络接口设备名,interval是报告输出的频率,count是报告的采样数。输出的%Util列就是网络使用率。


5.1应用性能改进的考虑

单次读写数据量小而网络读写量大的应用会消耗大量的系统态CPU,产生大量的系统调用。对于这类应用,减少系统态CPU的策略是减少网络读写的系统调用。此外,使用非阻塞的Java NIO而不是阻塞的java.net.Socket,减少处理请求和发送相应的线程数,也可以改善应用性能。从非阻塞Socket中读取数据的策略是,应用在每次读请求时尽可能多地读取数据。同样,当往Socket中写数据时,每个写调用应该尽可能多地写。


6、磁盘I/O使用率

对于有磁盘操作的应用来说,查找性能问题,就应该监控磁盘I/O。其中磁盘I/O使用率是最有用的监控数据。磁盘I/O使用率,即磁盘处于活动时间的百分比,磁盘在数据传输和处理命令(如寻道)时处于活动状态。磁盘利用率与资源争用程度成正比,与性能成反比。也就是说磁盘利用率越高,资源争用就越严重,性能也就越差,响应时间就越长。一般来说,如果磁盘利用率超过 70%,应用进程将花费较长的时间等待 I/O 完成,因为绝大多数进程在等待过程中将被阻塞或休眠。iostat
iostat使用示例 展开原码如果应用的磁盘I/O使用率高,就值得深入分析系统磁盘I/O子系统的性能,进一步查看它预期的负载量、磁盘服务时间、寻道时间以及服务I/O事件的时间。如果需要改善磁盘使用率,可以使用一些策略。从硬件和操作系统上看,下面是一些改进磁盘I/O使用率的策略:更快的存储设备。文件系统扩展到多个磁盘。操作系统调优使得可以缓存大量的文件系统数据结构。从应用角度看,任何减少磁盘活动的策略都是有帮助的,例如使用带缓存的输入输出流以减少读写操作次数,或在应用中集成缓存的数据结构以减少或消除磁盘交互。缓冲流减少了调用操作系统的次数从而降低系统态的CPU使用率。虽然这不会改善磁盘I/O性能,但可以使更多CPU周期用于应用的其他部分或者其他运行的应用。JDK提供了缓冲数据结构,也容易使用,如java.io.BufferedInputStream和java.io.BufferedOutputStream。关于磁盘性能,有一个经常被忽视的方法,就是检查磁盘缓存是否开启。有一些系统将磁盘缓存设置为禁用。开启磁盘缓存可以改善严重依赖磁盘I/O的应用的性能。然而,如果发现系统默认设置为禁用磁盘缓存,你应该加以注意,因为一旦开启磁盘缓存,意外电源故障可能会造成数据损坏。


7、其他命令行工具

sar(System Activity Reporter系统活动情况报告)是目前 Linux 上最为全面的系统性能分析工具之一,可以从多方面对系统的活动进行报告,包括:文件的读写情况、系统调用的使用情况、磁盘I/O、CPU效率、内存使用状况、进程活动及IPC有关的活动等。sar用法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: