本文接着
《【Android Linux内存及性能优化】(一) 进程内存的优化 - 堆段》
《【Android Linux内存及性能优化】(二) 进程内存的优化 - 栈段 - 环境变量 - ELF》
《【Android Linux内存及性能优化】(三) 进程内存的优化 - ELF执行文件的 数据段-代码段》
《【Android Linux内存及性能优化】(四) 进程内存的优化 - 动态库- 静态库》
《【Android Linux内存及性能优化】(五) 进程内存的优化 - 线程》
《【Android Linux内存及性能优化】(六) 系统内存的优化》
《【Android Linux内存及性能优化】(七) 程序内存泄漏检查工具》
程序性能的问题,有很多原因,需要对症下药。
导致软件性能低下,主要有下面3种原因:
程序的运算量很大,导致CPU 过于繁忙,CPU 是瓶颈。
可以运行 top 命令,如果某个进程的CUP 利用率很高,则说明CPU 是性能瓶颈。
程序需要大量的I/O ,读写文件、内存操作等,CPU 更多的是处于等待,I/O 部分成为程序性能瓶颈。
对于大量I/O 引起的程序性能问题,可以学习这篇文章: 《使用异步 I/O 大大提高应用程序的性能》
程序之间相互等待,结果CPU 利用率很低,但运行速度依然很慢,程序间的共享与死锁制约了程序的性能。
如果系统的CPU 利用率并不高,而且也不存在大量的I/O 操作,那么很有可能是多个线程之间相互等待造成的,这时就需要对程序进行大规模的重构。
通过proc 目录,能够了解到CPU 和I/O 设备的工作状况,从而能够帮助分析导致程序性能低下的原因。
ciellee@sh:~$ cat /proc/stat
累计时间 user nice system idle iowait irq softirq
cpu 3167348 29348 1381731 80468196 1075970 0 101955 0 0 0
cpu0 397571 3476 157335 10150112 70357 0 35871 0 0 0
cpu1 372224 3435 174119 10136025 51305 0 16722 0 0 0
cpu2 418781 3775 195023 10131454 32896 0 7197 0 0 0
cpu3 381767 4442 166107 10172208 33756 0 14511 0 0 0
cpu4 376322 3423 160088 10192147 30325 0 3210 0 0 0
cpu5 388663 3460 158360 10202394 29673 0 4082 0 0 0
cpu6 435006 3761 212003 9410045 713497 0 5559 0 0 0
cpu7 397011 3573 158693 10073807 114157 0 14800 0 0 0
intr 185329212 9 479 0 0 0 0 0 0 1 5007429 0 0 0 0 0 0 0 60384 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1954
ctxt 550116756
btime 1590973874
processes 536879
procs_running 1
procs_blocked 0
softirq 198450776 16075871 60900311 42960 3505868 1818711 0 1749208 63006387 0 51351460
CPU 后面数值分别代表着CPU 在不同状态下所用的时间,其单位为 jiffy (0.01s),7个数值的含义分别是:
由此可以推导出:
CPU 时间 = user + nice + idle + iowait + irq + softirq
CPU 的利用率 = 1 - (idle)/(user + system + nice + iowait + irq + softirq)
根据CPU 的利用率,可以知道当前系统的CPU 负载情况。
从这些数据,可以知道分析性能瓶颈在哪里:
程序代码有问题,导致占用了大量的CPU ,可以计算CPU 用户态利用率
CPU 用户态利用率 = (user + nice)/(user + system + nice + idle + iowait + irq + softirq)
程序代码调用了大量的系统调用,导致Linux 内核占用了大量的CPU
CPU 内核态利用率 = (system)/(user + system + nice + idle + iowait + irq + softirq)
系统和Flash 、内存等 有大量的交互和等待,从而导致系统性能下降
IO 利用率 = (iowait)/(user + system + nice + idle + iowait + irq + softirq)
ciellee@sh:~$ cat /proc/loadavg
1.97 2.14 1.74 2/1048 6774
上面数字中:
1.97 : 1分钟平均负载
2.14 : 5分钟平均负载
1.74 : 15分钟平均负载
2 : 在采样时刻,运行队列的任务的数量,与/proc/stat 的procs_running 表示相同意思
1048 : 在采样时刻,系统中活跃的任务的个数(不包括运行已经结束的任务)
6774 : 最大的 PID 值,包括轻量级进程,即线程
ciellee@sh:~$ cat /proc/51/stat
51 (migration/7) S 2 0 0 0 -1 69238848 0 0 0 0 0 11 0 0
-100 0 1 0 4 0 0 18446744073709551615 0 0 0 0 0 0 0
2147483647 0 0 0 0 17 7 99 1 0 0 0 0 0 0 0 0 0 0 0
每个参数解析如下;
pid = 50 进程(线程)pid号
comm = migration 应用程序或命名的名字
task_state=S 任务的状态:R:running,S:sleeping,D:disk sleep,
T:stopped,T:tracing stop,Z:zombie,X:dead
ppid = 2 父进程ID
pgid = 0 线程组号
sid = 0 该任务所在的会话组ID
ttnr = 0 该任务的tty终端的设备号,
tty_pgrp = -1 终端的进程组号,当前运行在该任务所在终端的前台任务(包括 shell 应用程序)的pid
task->flags = 69238848 进程标志位,查看该任务的特性
min_flt = 0 该任务不需要从硬盘拷数据而发生的缺页(次缺页)次数
cmin_flt = 0 累计的该任务的所有的waited-for 进程曾经发生的次缺页的数目
maj_flt = 0 该任务需要从硬盘拷数据而发生的缺页(主缺页)次数
cmaj_flt = 0 累计的该任务的所有的waited-for 进程曾经发生的主缺页的数目
utime = 0 该任务在用户态运行的时间,单位为 jiffy
stime = 11 该任务在核心态运行的时间,单位为 jiffy
cutime = 0 累计的该任务的所有的waited-for 进程曾经在用户态运行的时间,单位为 jiffy
cstime = 0 累计的该任务的所有的waited-for 进程曾经在核心态运行的时间,单位为 jiffy
priority = -100 任务的动态优先级
nice = 0 任务的静态优先级
num_threads = 1 该任务所在的线程组里线程的个数
it_real_value = 0 由于计时间隔导致下一个SIGALRM 发送进程的时延,以jiffy 为单位
start_time = 4 该任务的启动的时间,单位为 jiffy
vsize = 0 该任务的虚拟地址空间大小
rss = 0(page) 该任务当前驻留虚拟地址空间的大小
vsize = 18446744073709551615(bytes) 该任务的虚拟空间大小
rss = 0(page) 该任务当前驻留物理地址空间的大小
rlim = 0(bytes) 该任务能驻留物理地址空间的最大值
start_code = 0 该任务在虚拟地址空间的代码段的起始地址
end_code = 0 该任务在虚拟地址空间的代码段的结束地址
start_stack = 0 该任务在虚拟地址空间的栈的结束地址
kstkesp = 0 esp(32位堆栈指针)的当前值,与在进程的内核堆栈页得到的一致
kstkeip = 0 指向将要执行的指令的指针,EIP(32位指令指针)的当前值
pendingsig = 2147483647 待处理信号的位图,记录发产送给进程的普通信号
block_sig = 0 阻塞信号的位图
sigign = 0 忽略的信号的位图
sigcatch = 0 被俘获的信号的位图
wchan = 0 如果该进程是睡眠状态,该值给出调度的调用点
nswap = 17 被 swapped 的页数,当前没用
cnswap = 7 所有子进程被swapped 的页数和,当前没用
exit_signal = 99 该进程结束时,向父进程所发送的信号
task_cpu(task) = 1 运行在哪个CPU 上
task_rt_priority = 0 实时进程的相对优先级别
task_policy = 0 进程的调度策略,0:非实时进程,1:FIFO实时进程,2:RR实时进程
通过文件stat 的utime, stime, cutime 和 cstime 的数值,能够计算出进程的CPU 占用率。
要想获得CPU 占用率,需要两个采样点:
采样点1: 系统时间记为 sys1, 进程时间分别为:utime1,stime1,cutime1,cstime1
采样点2: 系统时间记为 sys2, 进程时间分别为:utime2,stime2,cutime2,cstime2
进程CPU 占用率 = ( (utime2 + stime2 - cutime2 - stime2) - (utime1 + stime1 - cutime1 - stime1) ) / (sys2 - sys1)
进程用户态占用率 = ( (utime2 - cutime2) - (utime1 - cutime1) ) / (sys2 - sys1)
进程内核态占用率 = ( (stime2 - stime2) - (stime1 - stime1) ) / (sys2 - sys1)
top 是最常用来监控系统范围内进程活动的工具,它提供运行在系统上的与CPU 关系最密切的进程列表,以及许多有意义的统计值(如负载平均,进程数量 以及 使用的 存储器和页面空间的数量)。
Tasks: 302 total, 1 running, 229 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.7 us, 1.6 sy, 0.0 ni, 93.2 id, 1.3 wa, 0.0 hi, 0.1 si, 0.0 st
KiB Mem : 16281312 total, 2675480 free, 2865172 used, 10740660 buff/cache
KiB Swap: 16605180 total, 16598768 free, 6412 used. 12037752 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3571 ciellee 20 0 2139236 191748 40460 S 23.5 1.2 96:04.80 compiz
1141 root 20 0 580232 185248 137744 S 17.6 1.1 41:17.93 Xorg
3315 ciellee 20 0 827724 302988 15876 S 11.8 1.9 1:40.13 hud-service
3515 ciellee 20 0 2036500 104556 49964 S 11.8 0.6 1:17.75 nautilus
2521 ciellee 20 0 2951112 209128 36744 S 5.9 1.3 112:49.49 WeChat.exe
3270 ciellee 20 0 516464 48528 18988 S 5.9 0.3 0:23.38 bamfdaemon
3341 ciellee 20 0 704340 120900 24328 S 5.9 0.7 7:01.88 unity-panel-ser
24020 ciellee 20 0 24928 4140 3404 R 5.9 0.0 0:00.03 top
1 root 20 0 175892 5764 3220 S 0.0 0.0 0:03.46 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.13 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq