近期遇到一个堆外内存导致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偏高告警的故障分析过程的更多相关文章

  1. 你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!

    GitHub 20k Star 的Java工程师成神之路,不来了解一下吗! GitHub 20k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 20k Star 的Java工 ...

  2. Linux(2)---记录一次线上服务 CPU 100%的排查过程

    Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...

  3. 记录一次线上bug

    记录一次线上bug,总的来说就是弱网和重复点击.特殊值校验的问题. 测试场景一:        在3g网络或者使页面加载速度需要两秒左右的时候,输入学号,提交学生的缴费项目,提交完一个 学生的缴费后, ...

  4. 【jvm】来自于线上的fullGC分析

    系统最近老年代的内存上升的比较快,三到四天会发生一波fullGC.于是开始对GC的情况做一波分析. 线上老年代2.7G,年轻带1.3G老年代上升较快,3天一波fullGC,并且fullGC会把内存回收 ...

  5. 原创 记录一次线上Mysql慢查询问题排查过程

    背景 前段时间收到运维反馈,线上Mysql数据库凌晨时候出现慢查询的报警,并把原始sql发了过来: --去除了业务含义的sql update test_user set a=1 where id=1; ...

  6. 利用JVM在线调试工具排查线上问题

    在生产上我们经常会碰到一些不好排查的问题,例如线程安全问题,用最简单的threaddump或者heapdump不好查到问题原因.为了排查这些问题,有时我们会临时加一些日志,比如在一些关键的函数里打印出 ...

  7. 记录一次线上实施snmp

    公司要实施一个部级的项目,我们公司的提供的产品要对接下客户的一个平台监控平台,该监控平台使用snmp,我们公司的产品不支持snmp,所以由我负责在现网实施snmp,记录这次现网 一.生成编译规则 1. ...

  8. JVM参数配置的线上教训

    原来规则处理业务五十台服务器经常大量fgc,load飙高,我修改了jvm配置后,五十台服务器十多天没有任何异常,双十一中轻闲度过. 可是今天突然又有一台大量fgc,load飙高.分析了半天,回头一看, ...

  9. 记录一次线上yarn RM频繁切换的故障

    周末一大早被报警惊醒,rm频繁切换 急急忙忙排查 看到两处错误日志 错误信息1 ervation <memory:0, vCores:0> 2019-12-21 11:51:57,781 ...

随机推荐

  1. mysql 排序长度限制之max_length_for_sort_data以及mysql两种排序算法

    SET max_length_for_sort_data = 1024 SHOW VARIABLES LIKE '%max_length_for_sort_data%'; 查询:SELECT * FR ...

  2. java 从 PKCS12(比如pfx格式)证书中提取私钥证书(PrivateKey)和受信任的公钥证书(X509Certificate)的序列号(SerialNumber)

    import lombok.Cleanup; import lombok.Getter; import lombok.Setter; import lombok.SneakyThrows; impor ...

  3. android ------ AndroidX的 Tablayout(com.google.android.material.tabs.TabLayout) 的使用

    前面呢,有写过TabLayout的博客,最近开发用到了AndroidX来解决前面的问题,不要工具类设置下划线的问题了,来总结一下 Android--------TabLayout实现新闻客户端顶部导航 ...

  4. easyui的datagrid的使用记录

    datagrid是在 table的基础上变化而来的, 而不是在div的基础上来的. 从div来变成 datagrid,样式的设置还是是比较麻烦的. dg=datagrid 的标题 来源于 column ...

  5. DSCP 标

    DSCP差分服务代码点(Differentiated Services Code Point).它在每个数据包IP头部的服务类别TOS标识字节中,利用已使用的6比特和未使用的2比特,通过编码值来区分优 ...

  6. windows下安装zk

    1 下载安装包并解压 2 修改conf下配置文件名字 zoo_sample.cfg 文件名改为 zoo.cfg 3 启动zk zKServer.cmd 4启动客户端连接测试 zKCli.cmd 127 ...

  7. http 1.1 状态码定义

    part of Hypertext Transfer Protocol -- HTTP/1.1RFC 2616 Fielding, et al. 10 Status Code Definitions ...

  8. ThinkPHP5中模型关联关系一对一,一对多

    TP5 返回json反斜杠前面转义了class XinDai extends Controller{ public function index(){ $res = [ ['logo'=>'/i ...

  9. VSCode调试data层时自身的一个bug

    使用VSCode在pytorch下进行调试有的时候会遇到下面这个错误: E00037.: Exception escaped from start_client Traceback (most rec ...

  10. CTS/APIO2019 游记

    已经记不得是第几天了就按顺序编号好了. 一 久违的到了北京,上次来北京还是在去年CTSC和APIO的时候.回想起来,这一年发生了很多事情啊. 下午是THUPC的试机赛,我和假雪菜.嘿嘿嘿两个神仙队友过 ...