java怎么查问题,java线上问题排查(日志、资源、代码定位)

  • Post author:
  • Post category:java


linux查询日志

在linux中可以使用head、tail等命令查看日志等文本文件,例如:

#显示文件前10行

head -n 10 catalina.out

#显示文件后10行

tail -n 10 catalina.out

#持续刷新

tail -f catalina.out

那么如果我们需要进行关键字查询,则需要使用grep命令;

grep后面有三个参数:A/B/C +数字;分别代表输出后N行、前N行、上下N行;

如果字符串中有空格或者特殊符号,如:”2020-03-30 14:57:24.301 INFO 4145 -“,则需要加上双引号:

grep -A 5 需要匹配的字符串 catalina.out

# 特殊符号

grep -A 5 “2020-03-30 14:57:24.301 INFO 4145 -” catalina.out

限制输出

如果查询的匹配项过多,而我们需要进行limit,则需要进行限制(-m参数):

#只获取前N个匹配项

grep -m 3 -A 10 需要查询的字符串 catalina.out

#只获取后N个匹配项,需要结合tac命令使用

tac catalina.out | grep -m 3 -A 10 需要查询的字符串

tac命令用于将文件以行为单位的反序输出,即第一行最后显示,最后一行先显示。

如果说只限制输出条数,可以结合head、tail命令使用:

grep -A 10 需要查询的字符串 catalina.out |head -n 100

grep -A 10 需要查询的字符串 catalina.out |tail -n 100

线程数查询

在web应用程序中,tomcat线程是一个需要关注的地方。

tomcat中Connector连接器负责创建请求和返回响应,默认只有200个并发(maxThread);当maxThread占满,线程会进行等待(等待线程数量acceptCount);如果超过这个数值,tomcat会放弃线程,并返回connection Refused 异常。

假设我们需要持续性的监控tomcat的线程数,可以按照以下流程:

# 查询PID

ps -ef|grep tomcat

# 根据Pid查询线程数

ps -Lf 找到的pid|wc -l

需要注意的是,tomcat线程并不能反映实际的连接情况,毕竟一个请求可能会运行多个子线程。

CPU占用以及代码定位

可以使用top或者htop查看CPU和内存消耗,htop命令更加直观和强大

# 更改刷新频率可以加d参数,单位-秒

top -d 1

# 单独查看进程加P参数

top -p PID

#根据ID查看进程详细信息

ps -aef|grep PID

假设一个java进程导致CPU飚高,我们需要根据top命令,通过进程ID找到那些高占用的线程。

这里主要关注CPU使用率以及线程执行时长两个因素。

#查询进程中的线,H参数要大写

top -p PID -H

d6a0fc4900e540e15ea690cc272ff34e.png

找到对应的线程后,需要将10进制的线程ID转换为16进制,并进行搜索

#线程ID转换为16进制得到线程标记(注意是线程ID)

printf “%x\n” 线程ID

#jstack 查询(注意jstack -l后面是进程ID,grep -A 50 匹配后50条输出)

jstack -l 26068 |grep -A 50 65f3

6ebe4aa2f078fc8135928dd64d966b4c.png

通过查看代码,发现的确是写了个死循环

1f9adcecfe018ce0a00dc9d3e1986fe1.png

内存占用情况以及代码定位

一般情况下,如果内存消耗线性增长,则需要考虑是否存在泄露问题导致GC无法回收造成的。

top命令同样会显示内存消耗,可以使用-a参数,或者键盘大写按下M键进行排序。

配合jvm的小工具jmap命令,可以扫描堆中对象信息,定位内存高占用的原因。

#查看内存占用排序( -ab是根据内存正排序,-n 代表执行一次)

top -ab -n 1 |head -n 20

#jmap命令输出堆信息,histo:live输出状态是live的对象

jmap -histo:live PID |head -n 20

通过下面图片可以看到,JVM中有一个自定义的实体类有500W+个实例,这显然是有问题的。

979e22fcc393d99c6400be6820281394.png

以上只是简单的查看问题,此时需要通过dump heap快照并通过mat、jprofiler等工具进行更深层次的分析。

存储空间查看

主要使用df和du命令:

df 查看文件系统内部的 inode

du 查看文件和目录磁盘使用情况

linux文件都必须有一个inode,因此有可能发生inode已经用光但硬盘还未存满的情况;这时就无法在硬盘上创建新文件。

相关blog:文件和inode关系资料

查看inode空间占用

df -h

df -h 命令默认是当前目录,可以使用 df -h /usr/ 查看指定目录

dedd521df6a517a5b99b3027623b0fb7.png

第1列是代表文件系统对应的设备文件的路径名。

第2、3、4列分别表示总共、已用、可用空间。

第3、4列之和会略小于第二列,主要是系统保留少量空间给管理员使用;当普通用户的容量占用100%时,管理员还可以登录系统操作。

查看磁盘空间占用

统计文件夹磁盘占用

#查看整体空间占用

du -sh /usr/

#当前目录-各个文件夹占用

du –max-depth=1 -h

#指定目录-各个文件夹占用

du –max-depth=1 -h /usr/

#指定目录-各个文件夹占用-并排序

du –max-depth=1 /usr/ |sort -nr

du –max-depth=1 /usr/ |sort -nr |head -n 前N行

2d189806dd0878250a8f93803f0cc06d.png

linux文件描述符(句柄)

句柄是windows中的概念,linux中主要指文件描述符。

句柄可以大致理解为:进程打开的文件的内存地址的引用。

在linux中,操作文件、创建socket连接(尤其注意通过http访问外部接口),都会占用句柄数。

每个进程所能占用的句柄数是有上限的,当进程打开文件、socket等超过了系统设置(或者是打开后未关闭),则会出现too many open files错误。

查看句柄数

linux对每个进程设置了允许最大操作的句柄数(默认1024);使用以下命令查看

ulimit -n

或者

more /proc/sys/fs/file-max

lsof命令

lsof即:list open files,常常用来查看进程打开了多少文件

统计进程使用的句柄总数

统计所有进程:

lsof -P -n |wc -l

查看所有进程打开的句柄数,并进行倒排序,取前十个

lsof -n|awk ‘{print $2}’|sort|uniq -c|sort -nr |head -n 10

cd68fda7b36bac8161d98b05b65c37d4.png

第一列是句柄数,第二列是进程ID

统计单个进程句柄:

lsof -p PID |wc -l

查看进程详细信息

根据上面查看到的进程ID,可以查看其对应的应用程序

ps -aef|grep 进程ID

或者根据PID查看程序运行目录

ll /proc/进程ID/cwd

查看进程打开的文件

lsof -p 进程PID

或者搜索指定文件

lsof -p 进程PID |grep 文件名

结合docker使用

查看容器的PID:

docker ps

docker top 容器ID

再根据上面的统计单个进程句柄命令进行容器句柄统计:

lsof -p PID |wc -l