Linux基本命令-Java线上问题排查相关

在这里插入图片描述
在这里插入图片描述

一、线上排查命令简介

jmap

  • jmap -heap pid 输出当前进程 JVM 堆新生代、老年代、持久代等请情况,GC 使用的算法等信息

  • jmap -histo:live {pid} | head -n 10 输出当前进程内存中所有对象包含的大小
    (输出当前进程内存中所有对象实例数 (instances) 和大小 (bytes), 如果某个业务对象实例数和大小存在异常情况,可能存在内存泄露或者业务设计方面存在不合理之处。)

  • jmap -dump:format=b,file=/usr/local/logs/gc/dump.hprof {pid} 以二进制输出档当前内存的堆情况,然后可以导入 MAT 等工具进行
    (也可以通过JVM参数配置OOM时自动dump当前内存镜像文件。 -XX:+HeapDumpOnOutOfMemoryError 和-XX:HeapDumpPath所代表的含义就是当程序出现OutofMemory时,将会在相应的目录下生成一份dump文件,而如果不指定选项-XX:HeapDumpPath则在当前目录下生成dump文件。
    确保应用发生 OOM 时 JVM 能够保存并 dump 出当前的内存镜像。
    当然,如果你决定手动 dump 内存时,dump 操作占据一定 CPU 时间片、内存资源、磁盘资源等,因此会带来一定的负面影响。
    此外,dump 的文件可能比较大 , 一般我们可以考虑使用 zip 命令对文件进行压缩处理,这样在下载文件时能减少带宽的开销。)

jstack

  • printf ‘%x\n’ tid :将线程id从10 进制转至 16 进制
  • jstack pid | grep tid -C 30 --color :利用 jstack 打印出 java 线程调用栈信息

某 Java 进程 CPU 占用率高,想要定位到其中 CPU 占用率最高的线程:
(1).先利用top命令找到CPU占用高的进程pid,也可以通过ps -ef | grep 应用名 来快速定位自己应用的pid。
(2).利用 top 命令可以查出占 CPU 最高的线程 pid (一个进程下有多个线程id),top -Hp pid。
(3).找到占用率最高的线程id后,将其转换为16进制,printf ‘%x\n’ tid 。
(4).利用 jstack 打印出 java 线程调用栈信息,jstack pid | grep tid -A 30 --color (其中pid为16进制格式)。

jinfo

  • jinfo -flags pid :查看pid对应的JVM参数

jinfo可以用来查看正在运行的java运用程序的扩展参数。
可以到 PerfMa : http://xxfox.perfma.com/jvm/check 校验参数的正确性。
在这里插入图片描述

jstat

jstat:Java Virtual Machine statistics monitoring tool JDK自带的一个轻量级小工具。

  • jstat -gc 12538 5000:即会每5秒一次显示进程号为12538的java进成的GC情况

参数说明如下:
S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC:年轻代中Eden(伊甸园)的容量 (字节)
EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC:Old代的容量 (字节)
OU:Old代目前已使用空间 (字节)
PC:Perm(持久代)的容量 (字节)
PU:Perm(持久代)目前已使用空间 (字节)
YGC:从应用程序启动到采样时年轻代中gc次数
YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
FGC:从应用程序启动到采样时old代(全gc)gc次数
FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)

二、CPU相关

关于cpu方面的问题,原因包括业务逻辑问题(死循环)、频繁gc以及上下文切换过多。而最常见的往往是业务逻辑(或者框架逻辑)导致的,可以使用jstack来分析对应的堆栈情况。

jstack分析CPU问题

  • 先用ps命令找到对应进程的pid(如果你有好几个目标进程,可以先用top看一下哪个占用比较高)。
  • top -H -p pid来找到cpu使用率比较高的一些线程。
  • 将占用最高的pid转换为16进制,printf ‘%x\n’ pid得到nid。
  • 在jstack中找到相应的堆栈信息jstack pid |grep ‘nid’ -C5 –color。

通常来说会比较关注WAITING和TIMED_WAITING的部分,BLOCKED就不用说了。我们可以使用命令cat jstack.log | grep “java.lang.Thread.State” | sort -nr | uniq -c来对jstack的状态有一个整体的把握,如果WAITING之类的特别多,那么多半是有问题。

频繁GC

可以先确定下gc是不是太频繁,使用jstat -gc pid 1000命令来对gc分代变化情况进行观察,如果看到gc比较频繁,再针对gc方面做进一步分析。

上下文切换

  • 针对频繁上下文问题,我们可以使用vmstat命令来进行查看,cs(context switch)一列则代表了上下文切换的次数。
  • 如果希望对特定的pid进行监控那么可以使用 pidstat -w pid命令,cswch和nvcswch表示自愿及非自愿切换。

三、磁盘

  • 首先是磁盘空间方面,我们直接使用df -hl来查看文件系统状态。
  • 更多时候,磁盘问题还是性能上的问题。我们可以通过iostatiostat -d -k -x来进行分析,最后一列%util可以看到每块磁盘写入的程度,而rrqpm/s以及wrqm/s分别表示读写速度,一般就能帮助定位到具体哪块磁盘出现问题了。
  • 另外还需要知道是哪个进程在进行读写,用iotop命令来进行定位文件读写的来源。这边拿到的是tid,我们要转换成pid,可以通过readlink来找到pidreadlink -f /proc/*/task/tid/…/… ,找到pid之后就可以看这个进程具体的读写情况cat /proc/pid/io 。

四、堆内内存

OOM

OOM大致可以分为以下几种:

  • Exception in thread “main” java.lang.OutOfMemoryError: unable to create new native thread
    意思是没有足够的内存空间给线程分配java栈,基本上还是线程池代码写的有问题,比如说忘记shutdown,所以说应该首先从代码层面来寻找问题,使用jstack或者jmap。如果一切都正常,JVM方面可以通过指定Xss来减少单个thread stack的大小。

  • Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
    意思是堆的内存占用已经达到-Xmx设置的最大值,应该是最常见的OOM错误了。解决思路仍然是先应该在代码中找,怀疑存在内存泄漏,通过jstack和jmap去定位问题。如果说一切都正常,才需要通过调整Xmx的值来扩大内存。

  • Caused by: java.lang.OutOfMemoryError: Meta space
    意思是元数据区的内存占用已经达到XX:MaxMetaspaceSize设置的最大值,排查思路和上面的一致,参数方面可以通过XX:MaxPermSize来进行调整。

Stack Overflow

  • Exception in thread “main” java.lang.StackOverflowError
    表示线程栈需要的内存大于Xss值,同样也是先进行排查,参数方面通过Xss来调整,但调整的太大可能又会引起OOM。

GC问题和线程

gc问题除了影响cpu也会影响内存,排查思路也是一致的。

  • 一般先使用jstat来查看分代变化情况,比如youngGC或者fullGC次数是不是太多;EU、OU等指标增长是不是异常等。
  • 线程的话太多而且不被及时gc也会引发oom,大部分就是之前说的unable to create new native thread。除了jstack细细分析dump文件外,我们一般先会看下总体线程,通过pstree -p pid |wc -l。

五、GC相关

堆内内存泄漏总是和GC异常相伴。不过GC问题不只是和内存问题相关,还有可能引起CPU负载、网络问题等系列并发症,只是相对来说和内存联系紧密些。
更多时候,我们是通过GC日志来排查问题的,在启动参数中加上-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps来开启GC日志。
下面将对G1垃圾收集器来做分析,-XX:+UseG1GC。

youngGC过频繁

youngGC频繁一般是短周期小对象较多,先考虑是不是Eden区/新生代设置的太小了,看能否通过调整-Xmn、-XX:SurvivorRatio等参数设置来解决问题。如果参数正常,但是young gc频率还是太高,就需要使用Jmap和MAT对dump文件进行进一步排查了。

youngGC耗时过长

耗时过长问题就要看GC日志里耗时耗在哪一块了。以G1日志为例,可以关注Root Scanning、Object Copy、Ref Proc等阶段。Ref Proc耗时长,就要注意引用相关的对象。Root Scanning耗时长,就要注意线程数、跨代引用。Object Copy则需要关注对象生存周期。而且耗时分析它需要横向比较,就是和其他项目或者正常时间段的耗时比较。

触发fullGC

G1中更多的还是mixedGC,但mixedGC可以和youngGC思路一样去排查。触发fullGC了一般都会有问题,G1会退化使用Serial收集器来完成垃圾的清理工作,暂停时长达到秒级别,可以说是半跪了。

fullGC的原因可能包括以下这些,以及参数调整方面的一些思路:

  • 并发阶段失败:在并发标记阶段,MixGC之前老年代就被填满了,那么这时候G1就会放弃标记周期。这种情况,可能就需要增加堆大小,或者调整并发标记线程数-XX:ConcGCThreads。
  • 晋升失败:在GC的时候没有足够的内存供存活/晋升对象使用,所以触发了Full GC。这时候可以通过-XX:G1ReservePercent来增加预留内存百分比,减少-XX:InitiatingHeapOccupancyPercent来提前启动标记,-XX:ConcGCThreads来增加标记线程数也是可以的。
  • 大对象分配失败:大对象找不到合适的region空间进行分配,就会进行fullGC,这种情况下可以增大内存或者增大-XX:G1HeapRegionSize。
  • 程序主动执行System.gc():不要随便写就对了。

另外,可以在启动参数中配置-XX:HeapDumpPath=/xxx/dump.hprof来dump fullGC相关的文件,并通过jinfo来进行gc前后的dump。

jinfo -flag +HeapDumpBeforeFullGC pid
jinfo -flag +HeapDumpAfterFullGC pid

这样得到2份dump文件,对比后主要关注被gc掉的问题对象来定位问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值