后端必备:如何排查问题以及jvm调优思路(一)

chujichenxuyuan
发布于 2022-5-16 18:02
浏览
0收藏

大家好,我是田螺。今天跟大家就CPU、磁盘、网络及内存方面的问题,聊聊如何排查和调优。


 •  CPU过高,怎么排查问题
 •  linux内存
 •  磁盘IO
 •  网络IO
 •  java 应用内存泄漏和频繁 GC
 •  java 线程问题排查
 •  常用 jvm 启动参数调优


linux CPU 过高,怎么排查问题


CPU 指标解析


 •  平均负载
      ◆ 平均负载等于逻辑 CPU 个数,表示每个 CPU 都恰好被充分利用。如果平均负载大于逻辑 CPU 个数,则负载比较重
 •  进程上下文切换
      ◆ 无法获取资源而导致的自愿上下文切换
      ◆ 被系统强制调度导致的非自愿上下文切换
 •  CPU 使用率
      ◆ 用户 CPU 使用率,包括用户态 CPU 使用率(user)和低优先级用户态 CPU 使用率(nice),表示 CPU 在用户态运行的时间百分比。用户 CPU 使用率高,通常说明有应用程序比较繁忙
      ◆ 系统 CPU 使用率,表示 CPU 在内核态运行的时间百分比(不包括中断),系统 CPU 使用率高,说明内核比较繁忙
      ◆ 等待 I/O 的 CPU 使用率,通常也称为 iowait,表示等待 I/O 的时间百分比。iowait 高,说明系统与硬件设备的 I/O 交互时间比较长
      ◆ 软中断和硬中断的 CPU 使用率,分别表示内核调用软中断处理程序、硬中断处理程序的时间百分比。它们的使用率高,表明系统发生了大量的中断

 

查看系统的平均负载

$ uptime
 10:54:52 up 1124 days, 16:31,  6 users,  load average: 3.67, 2.13, 1.79

 •10:54:52 是当前时间;up 1124 days, 16:31 是系统运行时间;6 users 则是正在登录用户数。而最后三个数字依次是过去 1 分钟、5 分钟、15 分钟的平均负载(Load Average)。平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数
 •当平均负载高于 CPU 数量 70% 的时候,就应该分析排查负载高的问题。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能
 •平均负载与 CPU 使用率关系
    ◆CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的
    ◆I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高
    ◆大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高


CPU 上下文切换


 •进程上下文切换:
    ◆进程的运行空间可以分为内核空间和用户空间,当代码发生系统调用时(访问受限制的资源),CPU 会发生上下文切换,系统调用结束时,CPU 则再从内核空间      ◆换回用户空间。一次系统调用,两次 CPU 上下文切换
    ◆系统平时会按一定的策略调用进程,会导致进程上下文切换
    ◆进程在阻塞等到访问资源时,也会发生上下文切换
    ◆进程通过睡眠函数挂起,会发生上下文切换
    ◆当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起
 •线程上下文切换:
    ◆同一进程里的线程,它们共享相同的虚拟内存和全局变量资源,线程上下文切换时,这些资源不变
    ◆线程自己的私有数据,比如栈和寄存器等,需要在上下文切换时保存切换
 •中断上下文切换:
    ◆为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件

 

查看系统的上下文切换情况:


vmstat 和 pidstat。vmvmstat 可查看系统总体的指标,pidstat则详细到每一个进程服务的指标

$ vmstat 2 1 
procs --------memory--------- --swap-- --io--- -system-- ----cpu----- 
r b swpd free    buff   cache  si so  bi bo in cs us sy id wa st 
1 0    0 3498472 315836 3819540 0 0   0  1  2  0  3  1  96 0  0

--------
cs(context switch)是每秒上下文切换的次数
in(interrupt)则是每秒中断的次数
r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数.当这个值超过了CPU数目,就会出现CPU瓶颈
b(Blocked)则是处于不可中断睡眠状态的进程数
# pidstat -w
Linux 3.10.0-862.el7.x86_64 (8f57ec39327b)      07/11/2021      _x86_64_        (6 CPU)

06:43:23 PM   UID       PID   cswch/s nvcswch/s  Command
06:43:23 PM     0         1      0.00      0.00  java
06:43:23 PM     0       102      0.00      0.00  bash
06:43:23 PM     0       150      0.00      0.00  pidstat

------各项指标解析---------------------------
PID       进程id
Cswch/s   每秒主动任务上下文切换数量
Nvcswch/s 每秒被动任务上下文切换数量。大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换
Command   进程执行命令

怎么排查 CPU 过高问题


 •先使用 top 命令,查看系统相关指标。如需要按某指标排序则 使用 top -o 字段名 如:top -o %CPU-o 可以指定排序字段,顺序从大到小

# top -o %MEM
top - 18:20:27 up 26 days,  8:30,  2 users,  load average: 0.04, 0.09, 0.13
Tasks: 168 total,   1 running, 167 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.5 sy,  0.0 ni, 99.1 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem:  32762356 total, 14675196 used, 18087160 free,      884 buffers
KiB Swap:  2103292 total,        0 used,  2103292 free.  6580028 cached Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND         
2323 mysql     20   0 19.918g 4.538g   9404 S 0.333 14.52 352:51.44 mysqld   
1260 root      20   0 7933492 1.173g  14004 S 0.333 3.753  58:20.74 java   
1520 daemon    20   0  358140   3980    776 S 0.333 0.012   6:19.55 httpd    
1503 root      20   0   69172   2240   1412 S 0.333 0.007   0:48.05 httpd                       
                   
---------各项指标解析---------------------------------------------------
第一行统计信息区
    18:20:27                     当前时间
    up 25 days, 17:29             系统运行时间,格式为时:分
    1 user                     当前登录用户数
    load average: 0.04, 0.09, 0.13  系统负载,三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值

Tasks:进程相关信息
    running   正在运行的进程数
    sleeping  睡眠的进程数
    stopped   停止的进程数
    zombie    僵尸进程数
Cpu(s):CPU相关信息
    %us:表示用户空间程序的cpu使用率(没有通过nice调度)
    %sy:表示系统空间的cpu使用率,主要是内核程序
    %ni:表示用户空间且通过nice调度过的程序的cpu使用率
    %id:空闲cpu
    %wa:cpu运行时在等待io的时间
    %hi:cpu处理硬中断的数量
    %si:cpu处理软中断的数量
Mem  内存信息  
    total 物理内存总量
    used 使用的物理内存总量
    free 空闲内存总量
    buffers 用作内核缓存的内存量
Swap 内存信息  
    total 交换区总量
    used 使用的交换区总量
    free 空闲交换区总量
    cached 缓冲的交换区总量

 •  找到相关进程后,我们则可以使用 top -Hp pid 或 pidstat -t -p pid 命令查看进程具体线程使用 CPU 情况,从而找到具体的导致 CPU 高的线程
     ◆ %us 过高,则可以在对应 java 服务根据线程ID查看具体详情,是否存在死循环,或者长时间的阻塞调用。java 服务可以使用 jstack
     ◆ 如果是 %sy 过高,则先使用 strace 定位具体的系统调用,再定位是哪里的应用代码导致的
     ◆ 如果是 %si 过高,则可能是网络问题导致软中断频率飙高
     ◆ %wa 过高,则是频繁读写磁盘导致的。


linux 内存


查看内存使用情况


 •  使用 top 或者 free、vmstat 命令

# top 
top - 18:20:27 up 26 days,  8:30,  2 users,  load average: 0.04, 0.09, 0.13
Tasks: 168 total,   1 running, 167 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.5 sy,  0.0 ni, 99.1 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem:  32762356 total, 14675196 used, 18087160 free,      884 buffers
KiB Swap:  2103292 total,        0 used,  2103292 free.  6580028 cached Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND         
2323 mysql     20   0 19.918g 4.538g   9404 S 0.333 14.52 352:51.44 mysqld   
1260 root      20   0 7933492 1.173g  14004 S 0.333 3.753  58:20.74 java       
....

 • bcc-tools 软件包里的 cachestat 和 cachetop、memleak
  ◆ achestat 可查看整个系统缓存的读写命中情况
  ◆ cachetop 可查看每个进程的缓存命中情况
  ◆ memleak 可以用检查 C、C++ 程序的内存泄漏问题


free 命令内存指标

# free -m 
                total used   free   shared  buffers  cached 
Mem:            32107 30414  1692   0       1962     8489 
-/+ buffers/cache:    19962  12144 
Swap:               0     0     0

 

• shared 是共享内存的大小, 一般系统不会用到,总是0
• buffers/cache 是缓存和缓冲区的大小,buffers 是对原始磁盘块的缓存,cache 是从磁盘读取文件系统里文件的页缓存
• available 是新进程可用内存的大小


内存 swap 过高


Swap 其实就是把一块磁盘空间或者一个本地文件,当成内存来使用。swap 换出,把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存。swap 换入,在进程再次访问这些内存的时候,把它们从磁盘读到内存中来

 

• swap 和 内存回收的机制
     ◆ 内存的回收既包括了文件页(内存映射获取磁盘文件的页)又包括了匿名页(进程动态分配的内存)
     ◆ 对文件页的回收,可以直接回收缓存,或者把脏页写回磁盘后再回收
     ◆ 而对匿名页的回收,其实就是通过 Swap 机制,把它们写入磁盘后再释放内存
• swap 过高会造成严重的性能问题,页失效会导致频繁的页面在内存和磁盘之间交换
     ◆ 一般线上的服务器的内存都很大,可以禁用 swap
     ◆ 可以设置 /proc/sys/vm/min_free_kbytes,来调整系统定期回收内存的阈值,也可以设置 /proc/sys/vm/swappiness,来调整文件页和匿名页的回收倾向

 

linux 磁盘I/O 问题


文件系统和磁盘


• 磁盘是一个存储设备(确切地说是块设备),可以被划分为不同的磁盘分区。而在磁盘或者磁盘分区上,还可以再创建文件系统,并挂载到系统的某个目录中。系统就可以通过这个挂载目录来读写文件
• 磁盘是存储数据的块设备,也是文件系统的载体。所以,文件系统确实还是要通过磁盘,来保证数据的持久化存储
• 系统在读写普通文件时,I/O 请求会首先经过文件系统,然后由文件系统负责,来与磁盘进行交互。而在读写块设备文件时,会跳过文件系统,直接与磁盘交互
• linux 内存里的 Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。内核就可以把分散的写集中起来(优化磁盘的写入)
• linux 内存里的 Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读写的数据。下次访问这些文件数据时,则直接从内存中快速获取,而不再次访问磁盘

 

磁盘性能指标


• 使用率,是指磁盘处理 I/O 的时间百分比。过高的使用率(比如超过 80%),通常意味着磁盘 I/O 存在性能瓶颈。
• 饱和度,是指磁盘处理 I/O 的繁忙程度。过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为 100% 时,磁盘无法接受新的 I/O 请求。
• IOPS(Input/Output Per Second),是指每秒的 I/O 请求数
• 吞吐量,是指每秒的 I/O 请求大小
• 响应时间,是指 I/O 请求从发出到收到响应的间隔时间


IO 过高怎么找问题,怎么调优


• 查看系统磁盘整体 I/O

# iostat -x -k -d 1 1
Linux 4.4.73-5-default (ceshi44)        2021年07月08日  _x86_64_        (40 CPU)

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.08     2.48    0.37   11.71  27.80   507.24  88.53   0.02     1.34   14.96    0.90   0.09   0.10
sdb      0.00     1.20    1.28   16.67  30.91   647.83  75.61   0.17     9.51    9.40    9.52   0.32   0.57
------ 
rrqm/s:   每秒对该设备的读请求被合并次数,文件系统会对读取同块(block)的请求进行合并
wrqm/s:   每秒对该设备的写请求被合并次数
r/s:      每秒完成的读次数
w/s:      每秒完成的写次数
rkB/s:    每秒读数据量(kB为单位)
wkB/s:    每秒写数据量(kB为单位)
avgrq-sz: 平均每次IO操作的数据量(扇区数为单位)
avgqu-sz: 平均等待处理的IO请求队列长度
await:    平均每次IO请求等待时间(包括等待时间和处理时间,毫秒为单位)
svctm:    平均每次IO请求的处理时间(毫秒为单位)
%util:    采用周期内用于IO操作的时间比率,即IO队列非空的时间比率

 •  查看进程级别 I/O

# pidstat -d
Linux 3.10.0-862.el7.x86_64 (8f57ec39327b)      07/11/2021      _x86_64_        (6 CPU)

06:42:35 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
06:42:35 PM     0         1      1.05      0.00      0.00  java
06:42:35 PM     0       102      0.04      0.05      0.00  bash
------
kB_rd/s   每秒从磁盘读取的KB
kB_wr/s   每秒写入磁盘KB
kB_ccwr/s 任务取消的写入磁盘的KB。当任务截断脏的pagecache的时候会发生
Command   进程执行命令

 •  当使用 pidstat -d 定位到哪个应用服务时,接下来则需要使用 strace 和 lsof 定位是哪些代码在读写磁盘里的哪些文件,导致IO高的原因

$ strace -p 18940 
strace: Process 18940 attached 
...
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0f7aee9000 
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0f682e8000 
write(3, "2018-12-05 15:23:01,709 - __main"..., 314572844 
) = 314572844 
munmap(0x7f0f682e8000, 314576896)       = 0 
write(3, "\n", 1)                       = 1 
munmap(0x7f0f7aee9000, 314576896)       = 0 
close(3)                                = 0 
stat("/tmp/logtest.txt.1", {st_mode=S_IFREG|0644, st_size=943718535, ...}) = 0 

 •  strace 命令输出可以看到进程18940 正在往文件 /tmp/logtest.txt.1 写入300m

$ lsof -p 18940 
COMMAND   PID USER   FD   TYPE DEVICE  SIZE/OFF    NODE NAME 
java  18940 root  cwd    DIR   0,50      4096 1549389 / 
… 
java  18940 root    2u   CHR  136,0       0t0       3 /dev/pts/0 
java  18940 root    3w   REG    8,1 117944320     303 /tmp/logtest.txt 
----
FD 表示文件描述符号,TYPE 表示文件类型,NODE NAME 表示文件路径

 •  lsof 也可以看出进程18940 以每次 300MB 的速度往 /tmp/logtest.txt 写入

已于2022-5-16 18:02:57修改
收藏
回复
举报
回复
    相关推荐