上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载

专心于Java范畴优质技能,欢迎重视

作者:fredalxin

线上毛病首要会包含 CPU、磁盘、内存以及网络问题,而大多数毛病或许会包含不止一个层面的问题,所以进行排查时分尽量四个方面顺次排查一遍。一起例如 jstack、jmap 等东西也是不囿于一个方面的问题的,基本上出问题便是 df、free、top 三连,然后顺次 jstack、jmap 服侍,详细问题详细剖析即可。

CPU

一般来讲咱们首要会排查 CPU 方面的问题。CPU 反常往往仍是比较好定位的。原因包含事务逻辑问题(死循环)、标签14频频 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

能够看到咱们现已找到了 nid 为 0x42 的仓库信息,接着只需仔细剖析一番即可。

当然更常见的是咱们对整个 jstack 文件进行剖析,一般咱们会比较重视 WAITING 和 TIMED_WAITING 的部分,BLOCKED 就不用说了。咱们能够运用指令cat jstack.log | grep "java.lang.Thread.State" | sort -nr | uniq -c来对 jstack 的状况有一个全体的掌握,假如 WAITING 之类的特别多,那么多半是有问题啦。

频频 gc

当然咱们仍是会运用 jstack 来剖析问题,但有时分咱们能够先承认下 gc 是不是太频频,运用jstat -gc pid 1000指令来对 gc 分代改变状况进行调查,1000 表明采样距离(ms),S0C/S1C、S0U/S1U、EC/EU、OC/OU、MC/MU 别离代表两个 Survivor 区、Eden 区、老时代、元数据区的容量和运用量。YGC/YGT、FGC/FGCT、GCT 则代表 YoungGc、FullGc 的耗时和次数以及总耗时。假如看到 gc 比较频频,再针对 gc 方面做进一步剖析,详细能够参阅一下 gc 章节的描绘。

上下文切换

针对频频上下文问题,咱们能够运用vmstat指令来进行检查

cs(context switch)一列则代表了上下文切换的次数。

假如咱们期望对特定的 pid 进行监控那么能够上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载运用 pidstat -w pid指令,cswch 和 nvcswch 表明自愿及非自愿切换。

磁盘

磁盘问题和 CPU 相同是归于比较根底的。首要是磁盘空间方面,咱们直接运用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

咱们还能够经过 lsof 指令来承认详细的文件读写状况lsof -p pid

内存

内存问题排查起来相比照 CPU 费事一些,场景也比较多。首要包含 OOM、GC 问题和堆外内存。一般来讲,咱们会先用free指令先来检查一发内存的各种状况。

堆内内存

内存问题大多还都是堆内内存问题。表象上首要分为 OOM 和 Stack Overflo。

OOM

JMV 中的内存不足,OOM 大致能够分为以下几种:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

这个意思是没有满足的内存空间给线程分配 Java 栈,基本上仍是线程池代码写的有问题,比方说忘掉 shutdown,所以说应该首要从代码层面来寻觅问题,运用 jstack 或许 jmap。假如一切都正常,JVM 方面能够经过指定Xss来削减单个 thread stack 的巨细。别的也能够在体系层面,能够经过修正/etc/security/limits.confnofile 和 nproc 来增大 os 对线程的约束

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来进行调整(这儿就不说 1.8 曾经的永久代了)。

Stack Overflow

栈内存溢出,这个咱们见到也比较多。

Exception in thread "main" java.lang.StackOverflowError

表明线程栈需求的内存大于 Xss 值,相同也是先进行排查,参数方面经过Xss来调整,但调整的太大或许又会引起 OOM。

运用 JMAP 定位代码内存走漏

上述关于 OOM 和 Stack Overflo 的代码排查方面,咱们一般运用 JMAPjmap -dum标签3p:format=b,file=filename pid来导出 dump 文件

经过 mat(Eclipse Memory Analysis Tools)导入 dump 文件进行剖析,内存走漏问题一般咱们直接选 Leak Suspects 即可,mat 给出了内存走漏的主张。别的也能够挑选 Top Consumers 来检查最大目标陈述。和线程相关的问题能够挑选 thread overview 进行剖析。除此之外便是挑选 Histogram 类概览来自己渐渐剖析,咱们能够搜搜 mat 的相关教程。

日常开发中,代码发生内存走漏是比较常见的事,并且比较荫蔽,需求开发者愈加重视细节。比方说每次恳求都 new 目标,导致许多重复创立目标;进行文件流操作但未正确封闭;手动不妥触发 gc;ByteBuffer 缓存分配不合理等都会形成代码 OOM。

另一方面,咱们能够在发动参数中指定-XX:+HeapDumpOnOutOfMemoryError来保存 OOM 时的 dump 文件。

gc 问题和线程

gc 问题除了影响 CPU 也会影响内存,排查思路也是共同的。一般先运用 jstat 来检查分代改变状况,比方 youngGC 或许 fullGC 次数是不是太多呀;EU、OU 等目标添加是不是反常呀等。

线程的话太多并且不被及时 gc 也会引发 oom,大部分便是之前说的unable to create new native thread。除了 jstack 细细剖析 dump 文件外,咱们一般先会看下整体线程,经过pstreee -p pid |wc -l。

或许直接经过检查/proc/pid/task的数量即为线程数量。

堆外内存

假如碰到堆外内存溢出,那可真是太不幸了。首要堆外内存溢出体现便是物理常驻内存添加快,报错的话视运用办法都不承认,假如因为运用 Netty 导致的,那过错日志里或许会呈现OutOfDirectMemoryError过错,假如直接是 DirectByteBuffer,那会报OutOfMemoryError: Direct buffer memory。

堆外内存溢出往往是和 NIO 的运用相关,一般咱们先经过 pmap 来检查下进程占用的内存状况pmap -x pid | sort -rn -k3 | head -30,这段意思是检查对应 pid 倒序前 30 大的内存段。这边能够再一段时刻后再跑一次指令看看内存添加状况,或许和正常机器比较可疑的内存段在哪里。

咱们假如承认有可疑的内存端,需求经过 gdb 来剖析gdb --batch --pid {pid} -ex "dump memory filename.dump {内存开始地址} {内存开始地址+内存块巨细}"

获取 dump 文件后可用 heaxdump 进行检查hexdump -C filename | less,不过大多数看到的都是二进制乱码。

NMT 是 Java7U40 引进的 HotSpot 新特性,合作 jcmd 指令我标签3们就能够看到详细内存组成了。需求在发动参数中参加 -XX:NativeMemoryTracking=summary 或许 -XX:NativeMemoryTracking=detail,会有稍微功能损耗。

一般关于堆外内存缓慢添加直到爆破的状况来说,能够先设一个基线jcmd pid VM.native_memory baseline。

然后等放一段时刻后再去看看内存添加的状况,经过jcmd pid VM.native_memory detail.diff(summary.diff)做一下 summary 或许 detail 等级的 diff。

能够看到 jcmd 剖析出来的内存十分详细,包含堆内、线程以及 gc(所以上述其他内存反常其实都能够用 nmt 来剖析),这边堆外内存咱们要点重视 Internal 的内存添加,假如添加十分显着的话那便是有问题了。

detail 等级的话还会有详细内存段的添加状况,如下图。

此外在体系层面,咱们还能够运用 strace 指令来监控内存分配 strace -f -e "brk,mmap,munmap" -p pid

这边内存分配信息首要包含了 pid 和内存地址。

不过其实上面那些操作也很难定位到详细的问题点,要害仍是要看过错日志栈,找到可疑的目标,搞清楚它的收回机制,然后去剖析对应的目标。比方 Di上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载rectByteBuffer 分配内存的话,是需求 full GC 或许手动 system.gc 来进行收回的(所以最好不要运用-XX:+DisableExplicitGC)。那么其实咱们能够盯梢一下 DirectByteBuffer 目标的内存状况,经过jmap -histo:live pid手动触发 fullGC 来看看堆外内存有没有被收回。假如被收回了,那么大概率是堆外内存自身分配的太小了,经过-XX:MaxDirectMemorySize进行调整。假如没有什么改变,那就要运用 jmap 去剖析那些不能被 gc 的目标,以及和 DirectByteBuffer 之间的引证关系了。

GC 问题

堆内内存走漏总是和 GC 反常相伴。不过 GC 问题不仅仅和内存问题相关,还有或许引起 CPU 负载、网络问题等系列并发症,仅仅相对来说和内存联络严密些,所以咱们在此独自总结一下 GC 相关问题。

咱们在 CPU 章介绍了运用 jstat 来获取当时 GC 分代改变信息。而更多时分,咱们是经过 GC 日志来排查问题的,在发动参数中加上-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps来敞开 GC 日志。

常见的 You上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载ng GC、Full GC 日志意义在此就不做赘述了。

针对 gc 日志,咱们就能大致推断出 youngGC 与 fullGC 是否过于频频或许耗时过长,然后对症下药。咱们下面将对 G1 废物收集器来做剖析,这边也主张咱们运用 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 则需求重视目标生计周期。并且耗时剖析它需求横向比较,便是和其他项目或许正常时刻段的耗时比较。比方说图中的 Root Scanning 和正常时刻段比添加较多,那便是起的线程太多了。

触发 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

jinfo -flag +HeapDumpBeforeFullGC pid

jinfo -flag +HeapDumpAfterFullGC pid

这样得到 2 份 dump 文件,比照后首要重视被 gc 掉的问题目标来定位问题。

网络

涉及到网络层面的问题一般都比较杂乱,场景多,定位难,成为了大多数开发的噩梦,应该是最杂乱的了。这儿会举一些比方,并从 tcp 层、运用层以及东西的运用等方面进行论述。

超时

超时过错大部分处在运用层面,所标签1以这块侧重了解概念。超时大体能够分为衔接超时和读写超时,某些运用衔接池的客户端结构还会存在获取衔接超时和闲暇衔接整理超时。

  • 读写超时。readTimeout/writeTimeout,有些结构叫做 so_timeout 或许 socketTimeout,均指的是数据读写超时。留意这边的超时大部分是指逻辑上的超时。soa 的超时指的也是读超时。读写超时一般都只针对客户端设置。
  • 衔接超时。connectio上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载nTimeout,客户端一般指与服务端树立衔接的最大时刻。服务端这边 connectionTimeout 就有些形形色色了,Jetty 中表明闲暇衔接整理时刻,Tomcat 则表明衔接坚持的最大时刻。
  • 其他。包含衔接获取超时 connectionAcquireTimeout 和闲暇衔接整理超时 idleConnectionTimeout。多用于运用衔接池或行列的客户端或服务端结构。

咱们在设置各种超时时刻中,需求承认的是尽量坚持客户端的超时小于服务端的超时,以确保衔接正常完毕。

在实践开发中,咱们关怀最多的应该是接口的读写超时了。

怎样设置合理的接口超时标签10是一个问题。假如接口超时设置的过长,那么有或许会过多地占用服务端的 tcp 衔接。而假如接口设置的过短,那么接口超时就会十分频频。

服务端接口分明 rt 下降,但客户端依然一向超时又是另一个问题。这个问题其实很简略,客户端到服务端的链路包含网络传输、排队以及服务处理等,每一个环节都或许是耗时的原因。

TCP 行列溢出

tcp 行列溢出是个相对底层的过错,它或许会形成超时、rst 等更表层的过错。因而过错也更荫蔽,所以咱们独自说一说。

如上图所示,这儿有两个行列:syns queue(半衔接行列)、accept queue(全衔接行列)。三次握手,在 server 收到 client 的 syn 后,把音讯放到 syns queue,回复 syn+ack 给 client,server 收到 client 的 ack,假如这时 accept queue 没满,那就从 syns queue 拿出暂存的信息放入 accept queue 中,否则按 tcp_abort_on_overflow 指示的履行。

tcp_abort_on_overflow 0 表明假如三次握手第三步的时分 accept queue 满了那么 server 丢掉 client 发过来的 ack。tcp_abort_on_overflow 1 则表明第三步的时分假如全衔接行列满了,server 发送一个 rst 包给 client,表明废掉这个握手进程和这个衔接,意味着日志里或许会有许多connection reset / connectio上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载n reset by peer。

那么在实践开发中,咱们怎样能快速定位到 tcp 行列溢出呢?

netstat 指令,履行 netstat -s | egrep "listen|LISTEN"

如上图所示,overflowed 表明全衔接行列溢出的次数,sockets dropped 表明半衔接行列溢出的次数。

ss 指令,履行 ss -lnt

上面看到 Send-Q标签1 表明第三列的 listen 端口上的全衔接行列最大为 5,榜首列 Recv-Q 为全衔接行列当时运用了多少。

接着咱们看看怎样设置全衔接、半衔接行列巨细吧:

全衔接行列的巨细取决于 min(backlog, somaxconn)。backlog 是在 socket 创立的时分传入的,somaxconn 是一个 os 等级的体系参数。而半衔接行列的巨细取决于 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。

在日常开发中,咱们往往运用 servlet 容器作为服务端,所以咱们有时分也需求重视容器的衔接行列巨细。在 Tomcat 中 backlog 叫做acceptCount,在 Jetty 里边则是acceptQueueSize。

RST 反常

RST 包表明衔接重置,用于封闭一些无用的衔接,一般表明反常封闭,差异于四次挥手。

在实践开发中,咱们往往会看到connection reset / connection reset by peer过错,这种情标签3况便是 RST 包导致的。

端口不存在

假如像不存在的端口宣布树立衔接 SYN 恳求,那么服务端发现自己并没有这个端口则会直接回来一个 RST 报文,用于中止衔接。

自动替代 FIN 停止衔接

一般来说,正常的衔接封闭都是需求经过 FIN 报文完成,可是咱们也能够用 RST 报文来替代 FIN,表明直接停止衔接。实践开发中,可设置 SO_LINGER 数值来操控,这种往往是成心的,来越过 TIMED_WAIT,供给交互功率,不闲就慎用。

客户端或服务端有一边发生了反常,该方向对端发送 RST 以奉告封闭衔接

咱们上面讲的 tcp 行列溢出发送 RST 包其实也是归于这一种。这种往往是因为某些原因,一方无法再能正常处理恳求衔接了(比方程序崩了,行列满了),然后奉告另一方封闭衔接。

接收到的 TCP 报文不在已知的 TCP 衔接内

比标签17如,一方机器因为网络真实太差 TCP 报文失踪了,另一方封闭了该衔接,然后过了良久收到了之前失踪的 TCP 报文,但因为对应的 TCP 衔接已不存在,那么会直接发一个 RST 包以便敞开新的衔接。

一方长时刻未收到另一方的承认报文,在一守时刻或重传次数后宣布 RST 报文

这种大多也和网络环境相关了,网络环境差或许会导致更多的 RST 报文。

之前说过 RST 报标签14文多会导致程序报错,在一个已封闭的衔接上读操作会报connection reset,而在一个已封闭的衔接上写操作则会报connection reset by peer。一般咱们或许还会看到broken pipe过错,这是管道层面的过错,表明对已封闭的管道进行读写,往往是在收到 RST,报出connection reset错后继续读写数据报的错,这个在 glibc 源码注释中也有介绍。

咱们在排查毛病时分怎样承认有 RST 包的存在呢?当然是运用 tcpdump 指令进行抓包,并运用 wireshark 进行简略剖析了。tcpdump -i en0 tcp -w xxx.cap,en0 表明监听的网卡。

接下来咱们经过 wireshark 翻开抓到的包,或许就能看到如下图所示,赤色的就表明 RST 包了。

TIME_WAIT 和 CLOSE_WAIT

TIME_WAIT 和 CLOSE_WAIT 是啥意思信任咱们都知道。

在线上时,咱们能够直接用指令netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'来检查 time-wait 和 close_wait 的数量

用 ss 指令会更快ss -ant | awk 上海的大学-亚搏官网手机版-亚搏官网手机版下载-亚搏官网登录下载'{++S[$1]} END {for(a in S) print a, S[a]}'

TIME_WAIT

time_wait 的存在一是为了丢掉的数据包被后边衔接复用,二是为了在 2MSL 的时刻范围内正常封闭衔接。它的存在其实会大大削减 RST 包的呈现。

过多的 time_wait 在短衔接频频的场景比较简略呈现。这种状况能够在服务端做一些内核参数调优:

#表明敞开重用。答应将TIME-WAIT sockets从头用于新的TCP衔接,默以为0,表明封闭

net.ipv4.tcp_tw_reuse = 1

#表明敞开TCP衔接中TIME-WAIT sockets的快速收回,默以为0,表明封闭

net.ipv标签204.tcp_tw_recycle = 1

#表明敞开重用。答应将TIME-WAIT sockets从头用于新的TCP衔接,默以为0,表明封闭

net.ipv4.tcp_tw_reuse = 1

#表明敞开TCP衔接标签5中TIME-WAIT sockets的快速收回,默以为0,表明封闭

net.ipv4.tcp_tw_recycle = 1

当然咱们不要忘掉在 NAT 环境下因为时刻戳紊乱导致数据包被回绝的坑了,别的的办法便是改小tcp_max_tw_buckets,超越这个数的 time_wait 都会被干掉,不过这也会导致报time wait bucket table overflow的错。

CLOSE_WAIT

close_wait 往往都是因为运用程序写的有问题,没有在 ACK 后再次建议 FIN 报文。close_wait 呈现的概率乃至比 time_wait 要更高,结果也更严峻。往往是因为某个当地堵塞住了,没有正常封闭衔接,然后渐渐地耗费完一切的线程。

想要定位这类问题,最好是经过 jstack 来剖析线程仓库来排查问题,详细可参阅上述章节。这儿仅举一个比方。

开发同学说运用上线后 CLOSE_WAIT 就一向增多,直到挂掉停止,jstack 后找到比较可疑的仓库是大部分线程都卡在了countdownlatch.await办法,找开发同学了解后得知运用了多线程可是确没有 catch 反常,修正后发现反常仅仅是最简略的晋级 sdk 后常呈现的class not found。

来历:Pipe 链接:https://fredal.xin/java-error-check?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutia标签20o.io


现在人工智能十分火爆,给咱们引荐一个十分合适小白入门的教程,点下面链接即可跳转。

https://www.captainbed.net/suga

Write a Comment

电子邮件地址不会被公开。 必填项已用 *标注