记一次线上coredump事故
1、事故背景
上周三凌晨,我负责的某个模块在多台机器上连续发生coredump,幸好发生在业务低峰期,而且该模块提供的功能也不是核心流程功能,所以对线上业务影响比较小。发生coredump后,运维收到报警后立马拉起了服务,服务宕机时间为3分钟左右。
2、事故分析
第二天立即组织了事故分析小组,对事故发生原因进行了排查,coredump的时候JVM保存了coredump文件,运维帮忙转换成了问题分析结果文件,如下
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 12288 bytes for committing reserved memory.
# Possible reasons:
# The system is out of physical RAM or swap space
# In 32 bit mode, the process size limit was hit
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Use 64 bit Java on a 64 bit OS
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#
# Out of Memory Error (os_linux.cpp:2756), pid=43471, tid=140153494918912
#
# JRE version: Java(TM) SE Runtime Environment (7.0_75-b13) (build 1.7.0_75-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.75-b04 mixed mode linux-amd64 compressed oops)
# Core dump written. Default location: /home/xiaoju/marketing-mall/core or core.43471 (max size 100000000 kB). To ensure a full core dump, try "ulimit -c unlimited" before s
tarting Java again
#
--------------- T H R E A D ---------------
Current thread (0x00007f8080013800): JavaThread "ThreadPool_2" [_thread_new, id=31832, stack(0x00007f7807375000,0x00007f7807476000)]
Stack: [0x00007f7807375000,0x00007f7807476000], sp=0x00007f7807474980, free space=1022k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x9a18da] VMError::report_and_die()+0x2ea
V [libjvm.so+0x4975ab] report_vm_out_of_memory(char const*, int, unsigned long, char const*)+0x9b
V [libjvm.so+0x81ebae] os::Linux::commit_memory_impl(char*, unsigned long, bool)+0xfe
V [libjvm.so+0x81ec6c] os::pd_commit_memory(char*, unsigned long, bool)+0xc
V [libjvm.so+0x8169da] os::commit_memory(char*, unsigned long, bool)+0x2a
V [libjvm.so+0x81d13d] os::pd_create_stack_guard_pages(char*, unsigned long)+0x6d
V [libjvm.so+0x953e1e] JavaThread::create_stack_guard_pages()+0x5e
V [libjvm.so+0x95a764] JavaThread::run()+0x34
V [libjvm.so+0x820b88] java_start(Thread*)+0x108
--------------- P R O C E S S ---------------
Java Threads: ( => current thread )
=>0x00007f8080013800 JavaThread "ThreadPool_2" [_thread_new, id=31832, stack(0x00007f7807375000,0x00007f7807476000)]
0x00007f8080013000 JavaThread "ThreadPool_1" [_thread_blocked, id=31828, stack(0x00007f780eff1000,0x00007f780f0f2000)]
0x00007f808000a000 JavaThread "ThreadPool_0" [_thread_blocked, id=31827, stack(0x00007f7807577000,0x00007f7807678000)]
0x00007f8080009800 JavaThread "ThreadPool_0" [_thread_blocked, id=31824, stack(0x00007f7807779000,0x00007f780787a000)]
0x00007f8424261000 JavaThread "dubboConsumer-100.69.174.35:7101-thread-619" daemon [_thread_blocked, id=31816, stack(0x00007f7807476000,0x00007f7807577000)]
0x00007f80c0012800 JavaThread "ThreadPool_0" [_thread_blocked, id=31803, stack(0x00007f780797b000,0x00007f7807a7c000)]
0x00007f83fc09d000 JavaThread "dubboConsumer-100.69.112.55:7101-thread-621" daemon [_thread_blocked, id=31800, stack(0x00007f7807678000,0x00007f7807779000)]
基本的意思是说内存空间不足导致malloc系统调用失败,同时我们还发现打印出来的coredump时的java线程数以ThreadPool(原有名称替换为ThreadPool)开头的线程数有30000多个,然后定位到“ThreadPool”的线程池的使用位置,发现近期的改动是将ThreadPoolExecutor该成了二方库的ThreadPoolExecutor。当时改动的原因是使用二方库线程池会将flag传递到线程内,方便排查问题。在使用方式上没有改动,每一次调用创建一个线程池,异步处理多个任务,同步等待拿到处理任务结果,调用shutdown方法。
我们发现了发生coredump的基本原因是发生了内存泄露,声明的线程没有及时回收导致线程占用空间过大,查看java启动参数还没有配置-Xss(thread stack size),所以对于linux 64位系统,jvm默认设置线程栈大小位1M,所以就ThreadPool开头的线程就已经占用了30多G的空间,线上的物理内存为128G,一般占用90到100G,所以ThreadPool开头的线程直接导致内存耗尽,进程coredump。
在明确了coredump发生的原因后,还要确认为什么创建了那么多以ThreadPool开头的线程,在查看二方库的ThreadPoolExecutor的构造函数后一切真相大白了。二方库的ThreadPoolExecutor继承了jdk的ThreadPoolExecutor,二方库的ThreadPoolExecutor的构造函数初始化了父类线程池,同时也创建了一个定时调度线程池,用于统计该线程池内部使用线程数,空闲线程数,任务处理数等一些基本日志信息,而shutdown没有重新父类的shutdown方法,导致线程池释放的时候只释放了父类的,没有释放掉统计线程池,所以每一次接口调用都会增加一个线程,而这个接口调用量不大,线程数就会慢慢堆积知道内存耗尽。分布式调用框架使用了robin的方式也就是轮训,所以在单位时间内每一台机器上的接口调用次数差不多,创建的统计线程池也差不多,因此这四台机器也就在相近的时间先后dump,自此,本次coredump事故发生的前因后果都浮出了水面。
3、反思
本次事故是由多个因素导致的:
- 原有设计不合理,使用线程池应尽量声明为static,本身线程池除了利用他的并行性,还为了避免线程创建销毁的开销
- 修改代码时,是否为等价修改,还是需要多留个心眼,特别是公司二方库,往往实现上逻辑不完备,该重写shutdown方法没有重写
总之,程序员在日常工作开发过程中,还是要敬畏每一行代码,认真仔细,养成良好的编码习惯。
记一次线上coredump事故的更多相关文章
- 【MySQL】记一次线上重大事故:二狗子竟然把线上数据库删了!!
写在前面 估计二狗子这几天是大姨夫来了,心情很郁闷,情绪也很低落,工作的时候也有点心不在焉.让他发个版本,结果,一行命令下去把线上的数据库删了!你没听错:是删掉了线上的数据库!运营那边顿时炸了锅:怎么 ...
- 记一次线上bug排查-quartz线程调度相关
记一次线上bug排查,与各位共同探讨. 概述:使用quartz做的定时任务,正式生产环境有个任务延迟了1小时之久才触发.在这一小时里各种排查找不出问题,直到延迟时间结束了,该任务才珊珊触发.原因主要就 ...
- 解Bug之路-记一次线上请求偶尔变慢的排查
解Bug之路-记一次线上请求偶尔变慢的排查 前言 最近解决了个比较棘手的问题,由于排查过程挺有意思,于是就以此为素材写出了本篇文章. Bug现场 这是一个偶发的性能问题.在每天几百万比交易请求中,平均 ...
- 记一次线上事故的JVM内存学习
今天线上的hadoop集群崩溃了,现象是namenode一直在GC,长时间无法正常服务.最后运维大神各种倒腾内存,GC稳定后,服务正常.虽说全程在打酱油,但是也跟着学习不少的东西. 第一个问题:为什么 ...
- 记一次线上Curator使用过程JVM栈溢出解决
为了同学们看起来一目了,特按如下思路进行讲解. 1.出现的场景 2.分析及解决的过程 3.总结 最近公司要使用zookeeper做配置管理(后面简称ZK),然后自己就提前用虚拟机进行 ...
- 记一次线上gc调优的过程
近期公司运营同学经常表示线上我们一个后台管理系统运行特别慢,而且经常出现504超时的情况.对于这种情况我们本能的认为可能是代码有性能问题,可能有死循环或者是数据库调用次数过多导致接口运行 ...
- 记一次线上MySQL数据库死锁问题
最近线上项目报了一个MySQL死锁(DealLock)错误,虽说对业务上是没有什么影响的,由于自己对数据库锁这块了解不是很多,之前也没怎么的在线上碰到过.这次刚好遇到了,便在此记录一下 ...
- 记一次线上Kafka消息堆积踩坑总结
2018年05月31日 13:26:59 xiaoguozi0218 阅读数:2018更多 个人分类: 大数据 年后上线的系统,与其他业务系统的通信方式采用了第三代消息系统中间件Kafka.由于是 ...
- 记一次线上由nginx upstream keepalive与http协议"协作"引起的接口报错率飙高事件
年前接到个任务,说要解决线上一些手机客户端接口报错率很高的问题.拿到了监控邮件,粗略一看,各种50%+的错误率,简直触目惊心.这种疑难杂症解决起来还是挺好玩的,于是撸起袖子action. 最终的结果虽 ...
随机推荐
- 13 获取外部数据库 以及数据库游标适配器(SimpleCursorAdapter)
获取外部数据库 API SQLiteDatabase db = SQLiteDatabase.openDatabase(path, factory, flags); 参数详解: path:数据库路径 ...
- UNIX网络编程——epoll 的accept , read, write(重要)
在一个非阻塞的socket上调用read/write函数,返回EAGAIN或者EWOULDBLOCK(注:EAGAIN就是EWOULDBLOCK). 从字面上看,意思是: EAGAIN: 再试一次 E ...
- Cocos2D中Action的进阶使用技巧(一)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 大家对Cocos2d中动作的使用大概都很清楚了,其实本身act ...
- (一一四)使用FMDB操作SQLite数据库
上节介绍了用系统自带的C语言库操作SQLite的方法,比较繁琐,使用FMDB会大幅度简化,并且是面向对象的,使用十分方便. 使用步骤如下: 先从github下载FMDB框架,然后把它导入工程. ①导入 ...
- Dynamics CRM 2015Online Update1 new feature之 插件跟踪日志
在最新的CRM2015Online Update1版本中加入了一个新功能-插件跟踪日志,与其说是新功能更应该说是对原有功能的加强,因为ITracingService这个接口在2013中已经引入了, ...
- 新手学python(2):C语言调用完成数据库操作
继续介绍本人的python学习过程.本节介绍如何利用python调用c代码.内容还是基于音乐信息提取的过程,架构如图一.Python调用c实现的功能是利用python访问c语言完成mysql数据库操作 ...
- (NO.00004)iOS实现打砖块游戏(一):素材的制作
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 本系列来实现一个儿时就很喜欢的打砖块游戏,我记得以前红白机上有一 ...
- Cocos2D将v1.0的tileMap游戏转换到v3.4中一例(五)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 为了暂时不影响原来的cat移动方法,我们在CatSprite.m ...
- 当freemarker中EL表达式的值为空时出现异常的解决方法
<#list pageView.list as msg> <form name="msgForm" id="msgForm" ...
- JAVA之旅(十八)——基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用
JAVA之旅(十八)--基本数据类型的对象包装类,集合框架,数据结构,Collection,ArrayList,迭代器Iterator,List的使用 JAVA把完事万物都定义为对象,而我们想使用数据 ...