Linux上快速定位Java代码问题行

生产环境中,经常会遇到CPU持续飙高或内存、IO飙高,如何快速定位问题点是很多新手头疼的问题,只能通过经验和代码推理,其实这里针对Java程序可以通过top和jstack命令,快速定位到问题代码。

Top命令的输出

具体定位之前,先补全一下top命令的输出解释,top 命令的输出是动态更新的,通常每3秒刷新一次,但这个刷新频率可以通过交互命令 d 来调整。看一个top命令的输出样例:

top - 16:45:26 up 5 days,  3:12,  1 user,  load average: 0.00, 0.01, 0.05
Tasks: 239 total,   1 running, 238 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.0 us,  0.6 sy,  0.0 ni, 98.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  16280104 total,  10346652 free,   2689256 used,   3256196 buff/cache
KiB Swap:   2097148 total,   2097148 free,        0 used.   7256332 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND      
 1234 user1     20   0 12345672 1048576  524288 S  0.7  6.4   2:34.16 java     
    1 root      20   0       0      0      0 S  0.0  0.0   0:03.16 init      
   72 www-data  20   0  344736   8192   7168 S  0.3  0.1   0:01.93 apache2     

top 命令的输出分为两部分:顶部的系统概览和底部的进程列表。

顶部的系统概览

  • 任务:显示系统中的进程总数,以及运行(R)、休眠(S)、停止(T)、僵尸(Z)状态的进程数量。
  • %Cpu(s):CPU使用率的百分比,通常分为用户空间(us)、系统空间(sy)、空闲(id)、等待I/O(wa)和其他(hi、si、st)。
  • KiB Mem:系统内存的使用情况,包括已使用(used)、空闲(free)、缓存(buffers/cached)和可用内存(available)。
  • KiB Swap:交换空间的使用情况,包括已使用(used)和空闲(free)。
  • 时间:显示当前时间、系统运行时间、平均负载(1分钟、5分钟、15分钟)。

底部的进程列表

  • PID:进程ID。
  • USER:进程所有者的用户名称。
  • PR:进程的优先级(数值越小,优先级越高)。
  • NI:进程的nice值(影响进程的优先级)。
  • VIRT:虚拟内存的大小,单位通常是KiB。
  • RES:常驻内存的大小,即进程实际占用的物理内存,单位通常是KiB。
  • SHR:共享内存的大小,单位通常是KiB。
  • S:进程的状态,如休眠(S)、运行(R)、停止(T)等。
  • %CPU:进程占用的CPU使用率百分比。
  • %MEM:进程占用的内存使用率百分比。
  • TIME+:进程占用CPU的总时间。
  • COMMAND:启动进程的命令名称或命令行。

在这个示例中,系统已经运行了5天3小时12分钟,平均负载在过去1分钟是0.00,过去5分钟是0.01,过去15分钟是0.05。CPU使用情况显示1%的用户空间使用率和0.6%的系统空间使用率,其余为空闲。内存方面,总共有16280104KiB的内存,其中10346652KiB是空闲的,2689256KiB被使用,3256196KiB作为缓存或缓冲。交换空间总共有2097148KiB,全部空闲。

进程列表显示了PID为1234的进程占用了较多的CPU和内存资源,它是以用户 user1 运行的,它是一个Java程序。

快读定位源代码步骤

通过top可以快速定位到资源利用异常的进程程序,这时候,如果是我们开发的Java程序占用较高的资源,但是又无法确认是具体哪一部分占用时,则可以通过 top命令和jstack进行具体代码的定位。思路如下:

首先,使用Top命令,发现存在问题的进程:

top - 10:05:00 up 3 days,  5:25,  1 user,  load average: 0.10, 0.15, 0.20
Tasks: 242 total,   2 running, 240 sleeping,   0 stopped,   0 zombie
%Cpu(s):  5.0 us,  1.0 sy,  0.0 ni, 94.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 16384M total,  8192M used,   8192M free,   2048M buff/cache
KiB Swap:  4096M total,     0M used,  4096M free.   4096M avail Mem 

 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM   TIME+ COMMAND      
23456 root     20   0 21G 15G  1024K S  0.5 91.5   1:50.01 java 
73455 root     20   0 0.2G 0.1G  1024K S  0.5 91.5   00:34.01 mysqld
11234 root     20   0 0.1G 0.15G 1024K S  0.5 91.5   1:45.03 sh

在这个例子中,PID为23456的 Java 进程使用了超过15GB的物理内存,并且占用了91.5%的内存资源。这可能意味着它正在消耗大量内存,可能会导致内存泄漏或其他内存使用问题。 

使用Top查看进程里的线程

获得了具体的问题进程后,通过 top -Hp [PID] 来查看其内部线程

top -Hp 23456

top - 10:15:00 up 3 days,  5:35,  1 user,  load average: 0.50, 0.40, 0.30
Tasks: 500 total,   2 running, 498 sleeping,   0 stopped,   0 zombie
%Cpu(s): 10.0 us,  2.0 sy,  0.0 ni, 88.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 16384M total,  9000M used,  7384M free,   2048M buff/cache
KiB Swap:  4096M total,     0M used,  4096M free.   7384M avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND      
 23457 root     20   0 21G 15G 1024K S  5.0 91.5   1:50.01 java        
 23457 root     20   0  4G 3.5G  8192K R 10.0  2.1   0:05.20 java        
 23458 root     20   0  4G 3.5G  8192K S  3.0  2.1   0:04.30 java        
 23459 root     20   0  4G 3.5G  8192K S  2.0  2.1   0:03.40 java        

将线程ID转为16进制

找到对应异常的线程,使用16进制转换命令 printf "0x%x" [线程ID],获取其线程16进制编码

[root@10]# printf "0x%x" 23457
0x5ba1[root@10]#

jstack跟踪堆栈定位代码

拿到了线程16进制编码,则通过java工具内置的jstack命令进行代码定位:

jstack [PID] | grep [线程16进制编码] -A 5

# 数字5表示输出栈顶最前几行代码

jstack 23456 | grep 0x5ba1 -A 5

"Thread-3" #23458 prio=5 os_prio=31 cpu=33.42ms elapsed=6.25s tid=0x5BEA runnable
   java.lang.Thread.State: RUNNABLE
        at java.util.HashMap.get(HashMap.java:628)
        at com.example.MyApplication.doSomething(MyApplication.java:42)
        at com.example.MyApplication.access$000(MyApplication.java:18)
        at com.example.MyApplication$1.run(MyApplication.java:77)
        at java.lang.Thread.run(Thread.java:748)
  • 35
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值