背景
开发过程中发现机器指标异常,端口也hang住无响应,端口返回为timeout,对应探活检测也失败了。
现象
在st测试环节,突然每隔一段时间新接口就hang住无响应,观察机器监控也发现端口探活失败,看机器指标也无上报。
原因
开发新注入了bean,导致metaspace超出分配区间,频繁fullgc导致机器无响应。
解决
定位到时metaspace分配不足,通过调大分配空间解决。
定位过程
一般定位方向有两个,通过看机器指标就可以发现:
- cpu方面:有死循环或线程过多,导致cpu被占用无法响应其他请求。
- 内存方面:内存泄露导致频繁gc或fullgc。
- 查看机器指标:查看发现业务cpu占用无明显升高,有些许降低,元空间占用情况升高。
- 查看机器gc日志:发现机器一直在fullgc
- 查看机器堆栈dump信息:发现堆内存比分配的小得多,但gc日志显示每次gc的减小量都很小。所以大概猜到是元空间的问题了。
- 查看应用的启动参数,发现metaspace使用空间大于分配空间:应用启动时会分配jvm的内存分配情况,参数是-XX:MaxMetaspaceSize=XXX。发现分配了256M,高的时候就占了250+M了,很明显是新开发代码新注入的bean导致的,通过增大元空间参数解决。
一些扩展知识
端口探活方式
- telnet <ip> <port>:telnet打通=端口通+网络通。只能分析 服务是否起来 或 网络是否正常 或 是否有防火墙等类似问题。此时服务可能不可用,但端口是通的。
- nc -zv <ip> <port>:
-z
只扫描端口,不发送数据,-v
显示详细信息。端口存活会显示 succeeded 或 open。 - 对于rpc框架,可以自定义端口的探活方式,通过服务端实现
healthCheck()
或ping()方法实现探活。
机器gc日志指标
- 开启机器gc记录的指令
jdk8之前:
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:/path/to/gc.log
jdk8之后:
-Xlog:gc*:file=/path/to/gc.log:time,uptime,level,tags
- 机器gc日志存放位置:在Xloggc对应的路径下
机器jvm内存使用情况
- 查看堆内存情况,每秒1次,输出10次:jstat -heap <PID> 1000 10
- 查看metaspace情况:
jstat -gcmetacapacity <pid>
jstat内存输出各指标含义:
指标 | 含义 |
---|---|
S0C | Survivor 0 区的容量(单位:KB) |
S1C | Survivor 1 区的容量(单位:KB) |
S0U | Survivor 0 区已使用空间(单位:KB) |
S1U | Survivor 1 区已使用空间(单位:KB) |
EC | Eden 区的容量(单位:KB) |
EU | Eden 区已使用空间(单位:KB) |
OC | Old 区(老年代)容量(单位:KB) |
OU | Old 区(老年代)已使用空间(单位:KB) |
PC | Perm 区(永久代)容量(单位:KB)(JDK8之前)或 Metaspace 容量(JDK8及以后,部分实现中) |
PU | Perm 区(永久代)已使用空间(单位:KB)(JDK8之前)或 Metaspace 已用(JDK8及以后,部分实现中) |
YGC | 从 JVM 启动到采样时发生的 Young GC(新生代GC)次数 |
YGCT | 从 JVM 启动到采样时 Young GC 所用时间(单位:秒) |
FGC | 从 JVM 启动到采样时发生的 Full GC(老年代GC)次数 |
FGCT | 从 JVM 启动到采样时 Full GC 所用时间(单位:秒) |
GCT | 从 JVM 启动到采样时 GC 总耗时(单位:秒),等于 YGCT + FGCT |
机器线程情况分析
堆栈dump指令,只能用来分析进程中的线程情况。
jstack -<PID>
jstack的作用:jstack
是 JDK 自带的一个非常重要的命令行工具,主要用于生成 Java 进程的线程快照(线程堆栈信息),帮助开发者排查和分析 Java 应用中的线程相关问题。但无法得到堆内存,元空间等内存信息。
metaspace
- 作用:是jdk8后取代永久代的空间,用于存放类的元数据(类结构,类方法,常量池等)。
- 监控的指令:
jmap -clstats <pid>
jstat -gcmetacapacity <pid>