【JVM】记录一次线上SWAP偏高告警的故障分析过程
近期遇到一个堆外内存导致swap飙高的问题,这类问题比较罕见,因此将整个排查过程记录下来了
现象描述
最近1周线上服务器时不时出现swap报警(swap超过内存10%时触发报警,内存是4G,因此swap超过400M会触发报警),每次都是童鞋们通过重启tomcat解决的;
但导致的根本原因是什么呢?必须找到根本原因才行,总是这么重启就有点low了
问题排查
于是找了1台占用了swap但还未触发报警的服务器进行了排查
以下是当时通过top命令观察到的结果
23:03:22 swap占用了354M的内存
23:55:42 swap占用了398M的内存
原因分析
到底是什么原因导致swap飙高呢?肯定是tomcat,因为每次重启tomcat就解决了;但根本原因是?
谁占用了swap
通过以下脚本 swap.sh
#!/bin/bash
# Get current swap usage for all running processes
# Erik Ljungstrom //
do_swap () {
SUM=
OVERALL=
for DIR in `find /proc/ -maxdepth -type d | egrep "^/proc/[0-9]"` ; do
PID=`echo $DIR | cut -d / -f `
PROGNAME=`ps -p $PID -o comm --no-headers`
for SWAP in `grep Swap $DIR/smaps >/dev/null| awk '{ print $2 }'`
do
let SUM=$SUM+$SWAP
done
echo "PID=$PID - Swap used: $SUM - ($PROGNAME )"
let OVERALL=$OVERALL+$SUM
SUM= done
echo "Overall swap used: $OVERALL"
}
do_swap |awk -F[\ \(] '{print $5,$1,$8}' | sort -n | tail -
可以看出PID=19911这个进程使用了324M的swap
通过grep进程号19911可以看出确实是tomcat占用swap最多
进程19911占用总的物理内存是3.1G,java占用的堆内内存大小为2.78G,剩下的320M是堆外内存占用的
Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss]
2779M=2048M+268M+463*1M
sudo -u tomcat ./jinfo -flag MaxPermSize
-XX:MaxPermSize= java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
intx CompilerThreadStackSize = {pd product}
intx ThreadStackSize = {pd product}
intx VMThreadStackSize = {pd product}
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) -Bit Server VM (build 24.45-b08, mixed mode) java -XX:+PrintFlagsFinal -version | grep -i permsize
uintx AdaptivePermSizeWeight = {product}
uintx MaxPermSize = {pd product}
uintx PermSize = {pd product}
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) -Bit Server VM (build 24.45-b08, mixed mode)
哪行代码占用了堆外内存
堆内内存溢出可以直接通过MAT分析堆信息就可以定位到具体的代码,但是对于堆外内存就必须通过BTrace来解决
google-perftools 定位类名和方法名
如何安装和使用google-perftools见这里
由于要启动google-perftools需要重启tomcat,所以重启tomcat后,PID从19911变成了9176
重启tomcat后,会自动生成heap文件,文件名的命名规范是gperf_pid.xxx.heap,所以我们只需要关注gperf_9176.*即可
[xxxx@xxxx /home/xxx/logs]$ ll *.heap
…...
-rw-r--r-- tomcat tomcat May : gperf_9171..heap
-rw-r--r-- tomcat tomcat May : gperf_9173..heap
-rw-r--r-- tomcat tomcat May : gperf_9174..heap
-rw-r--r-- tomcat tomcat May : gperf_9175..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
-rw-r--r-- tomcat tomcat May : gperf_9176..heap
…...
分析heap文件
/home/google-perftools/bin/pprof --text /home/java /home/logs/gperf_9176..heap
Using local file /home/java.
Using local file /home/logs/gperf_9176..heap.
Total: 186.4 MB
91.2 48.9% 48.9% 91.2 48.9% updatewindow
52.5 28.2% 77.1% 52.5 28.2% os::malloc
38.0 20.4% 97.4% 38.0 20.4% inflateInit2_
3.0 1.6% 99.0% 3.0 1.6% init
0.8 0.4% 99.5% 0.8 0.4% ObjectSynchronizer::omAlloc
0.4 0.2% 99.7% 0.4 0.2% readCEN
0.3 0.2% 99.9% 38.3 20.5% Java_java_util_zip_Inflater_init
0.1 0.1% 100.0% 0.1 0.1% _dl_allocate_tls
0.0 0.0% 100.0% 0.0 0.0% _dl_new_object
0.0 0.0% 100.0% 1.1 0.6% Thread::Thread
0.0 0.0% 100.0% 0.0 0.0% CollectedHeap::CollectedHeap
0.0 0.0% 100.0% 0.0 0.0% Events::init
0.0 0.0% 100.0% 0.4 0.2% ZIP_Put_In_Cache0
0.0 0.0% 100.0% 0.0 0.0% read_alias_file
0.0 0.0% 100.0% 0.0 0.0% _nl_intern_locale_data
可以看出是java.util.zip.Inflater的init()占用了比较多的内存
通过BTrace定位代码调用方
编写代码BtracerInflater.java对init方法进行拦截
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*; import java.nio.ByteBuffer;
import java.lang.Thread; @BTrace public class BtracerInflater{
@OnMethod(
clazz="java.util.zip.Inflater",
method="/.*/"
)
public static void traceCacheBlock(){
println("Who call java.util.zip.Inflater's methods :");
jstack();
}
}
运行BTrace
[xxxx@l-xxx.xx.xx /home/xxx/btrace-bin/bin]$ sudo -u tomcat ./btrace -cp ../build BtracerInflater.java|more
Who call java.util.zip.Inflater's methods :
java.util.zip.Inflater.<init>(Inflater.java:)
java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:)
java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:)
com.xxx.OrderDiffUtil.ungzip(OrderDiffUtil.java:)
com.xxx.OrderDiffUtil.parse(OrderDiffUtil.java:)
com.xxx.FaxOrderEventListener.takeSectionChangedInfo(FaxOrderEventListener.java:)
com.xxx.FaxOrderEventListener.onMessage(FaxOrderEventListener.java:)
.......
可以看出是OrderDiffUtil的ungzip()调用了java.util.zip.Inflater的init()
看看OrderDiffUtil.ungzip()
private static String ungzip(String encodeJson) {
ByteArrayOutputStream out = new ByteArrayOutputStream(encodeJson.length() * 5);
ByteArrayInputStream in = null;
try {
in = new ByteArrayInputStream(Base64.decode(encodeJson));
} catch (UnsupportedEncodingException e) {
return "{}";
}
try {
GZIPInputStream gunzip = new GZIPInputStream(in);
byte buffer[] = new byte[1024];
int len = 0;
while ((len = gunzip.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
return "{}";
}
try {
return out.toString("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
}
return "{}";
}
可见gunzip未被close
所以根本原因是未调用GZIPInputStream的close()关闭流导致堆外内存占用
【JVM】记录一次线上SWAP偏高告警的故障分析过程的更多相关文章
- 你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!
GitHub 20k Star 的Java工程师成神之路,不来了解一下吗! GitHub 20k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 20k Star 的Java工 ...
- Linux(2)---记录一次线上服务 CPU 100%的排查过程
Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...
- 记录一次线上bug
记录一次线上bug,总的来说就是弱网和重复点击.特殊值校验的问题. 测试场景一: 在3g网络或者使页面加载速度需要两秒左右的时候,输入学号,提交学生的缴费项目,提交完一个 学生的缴费后, ...
- 【jvm】来自于线上的fullGC分析
系统最近老年代的内存上升的比较快,三到四天会发生一波fullGC.于是开始对GC的情况做一波分析. 线上老年代2.7G,年轻带1.3G老年代上升较快,3天一波fullGC,并且fullGC会把内存回收 ...
- 原创 记录一次线上Mysql慢查询问题排查过程
背景 前段时间收到运维反馈,线上Mysql数据库凌晨时候出现慢查询的报警,并把原始sql发了过来: --去除了业务含义的sql update test_user set a=1 where id=1; ...
- 利用JVM在线调试工具排查线上问题
在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因.为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出 ...
- 记录一次线上实施snmp
公司要实施一个部级的项目,我们公司的提供的产品要对接下客户的一个平台监控平台,该监控平台使用snmp,我们公司的产品不支持snmp,所以由我负责在现网实施snmp,记录这次现网 一.生成编译规则 1. ...
- JVM参数配置的线上教训
原来规则处理业务五十台服务器经常大量fgc,load飙高,我修改了jvm配置后,五十台服务器十多天没有任何异常,双十一中轻闲度过. 可是今天突然又有一台大量fgc,load飙高.分析了半天,回头一看, ...
- 记录一次线上yarn RM频繁切换的故障
周末一大早被报警惊醒,rm频繁切换 急急忙忙排查 看到两处错误日志 错误信息1 ervation <memory:0, vCores:0> 2019-12-21 11:51:57,781 ...
随机推荐
- getaddrinfo工作原理分析
getaddrinfo工作原理分析 将域名解析成ip地址是所有涉及网络通讯功能程序的基本步骤之一,常用的两个接口是gethostbyname和getaddrinfo,而后者是Posix标准推荐在新应用 ...
- 【转】Rocketmq整体分析
原文:https://www.cnblogs.com/mantu/p/6108645.html 之前本人在实际的生产环境中,使用过activemq和rabbitmq消息队列,在使用过程中出现一些难以解 ...
- Vue使用NProgress
NProgress是页面跳转是出现在浏览器顶部的进度条官网:http://ricostacruz.com/nprogress/github:https://github.com/rstacruz/np ...
- CentOS7下安装ELK(nginx 、elasticsearch-5.1.1、logstash-5.1.1、kibana-5.1.1)
nginx: #直接yum安装: [root@elk-node1 ~]# yum install nginx -y 官方文档:http://nginx.org/en/docs/http/ngx_htt ...
- redhat 6安装python 3.7.4报错ModuleNotFoundError: No module named '_ctypes' make: *** [install] Error 1
问题描述: 今天在测试环境中,为了执行脚本,安装下python3命令,在执行make install的时候报错: ModuleNotFoundError: No module named '_ctyp ...
- 关于OpenGPU.org
今天是心情沉重的一天. OpenGPU.org,作为当年中国图形学界首屈一指的论坛,曾经创造过日访问破万的记录,而且汇聚了中国所有的图形行业的精英,大家畅所欲言,为整个中国图形学业界分享了无数宝贵的资 ...
- IDEA强制清除Maven缓存
目录 重新导入依赖的常见方式 存在的问题 彻底清除IDEA缓存的方式 重新导入依赖的常见方式 下面图中的刷新按钮,在我的机器上,并不能每次都正确导入pom.xml中写的依赖项,而是导入之前pom.xm ...
- c# Mono.Cecil IL方式 读MethodBody
using Kufen.Common.Definitions; using Mono.Cecil; using System; using System.Collections.Generic; us ...
- echo的色彩处理
在Shell脚本中,可以使用echo的-e选项使显示内容呈现出不同的颜色. 格式1:echo -e "\033[背景颜色代码;文字颜色代码m 输出的字符串 \033[0m" 格式2 ...
- [ ceph ] CEPH 部署完整版(CentOS 7 + luminous)
1. 前言 拜读了 胖哥的(el7+jewel)完整部署 受益匪浅,目前 CEPH 已经更新到 M 版本,配置方面或多或少都有了变动,本博文就做一个 ceph luminous 版本完整的配置安装. ...