linux 使用jstack_案例解析:线程池使用不当导致的系统崩溃
前几天,发现一台阿里云服务器上的Web服务不可用。远程SSH登录不上,尝试几次登录上去之后,执行命令都显示
-bash: fork: Cannot allocate memory
一看以为是内存泄漏导致溢出。因为执行不了任何命令, 只能通过控制台重启服务器恢复服务。
初步排查
服务恢复后,查看系统日志,linux系统日志路径/var/log/messages,可通过journalctl命令查看,如
journalctl --since="2019-06-12 06:00:00" --until="2019-06-12 10:00:00"`
可查看since之后,until之前时间段的日志。除了发现crond[14954]: (CRON) CAN'T FORK (do_command): Cannot allocate memory 这个错误日志,未见其它异常(下面的sshd[10764]: error: fork: Cannot allocate memory应是ssh登录执行命名失败的日志)
通过阿里云-云监控-主机监控查看内存使用率指标,这段时间内,内存使用率一直在40%以下,基本可排除内存溢出的可能。
通过搜索查阅到进程数超过操作系统限制可能导致bash: fork: Cannot allocate memory的报错(参考: https://blog.csdn.net/wangshuminjava/article/details/80603847 )。通过ps -eLf|wc -l查看当前进程线程数(ps -ef只打印进程,ps -eLf会打印所有的线程), 只有1000多个,故障时刻系统到底运行了多少线程已无从得知,只能持续跟进监测。
问题定位
几天后,再次通过ps -eLf|wc -l查看,发现线程数已达16000多个。直接执行ps -eLf可看到大量tomcat进程所产生的线程,猜测是不是线程死锁导致大量线程未完成一直hung在那里。
执行 jstack 进程号 > ~/jstack.txt 命令将进程所运行线程情况打印出来分析,发现大量的WAITING状态的线程,如下
"pool-19-thread-1" #254 prio=5 os_prio=0 tid=0x00007f0b700a6000 nid=0x29a9 waiting on condition [0x00007f0b274df000] java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x00000006ce3d8790> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)
根据上述内容可看出线程在等一个条件,并且是在执行LinkedBlockingQueue.take方法的时候,查看该方法的java doc,当队列为空时,该方法将会一直等待直到有元素可用。
/** * Retrieves and removes the head of this queue, waiting if necessary * until an element becomes available. * * @return the head of this queue * @throws InterruptedException if interrupted while waiting */E take() throws InterruptedException;
询问同事在哪里用到了LinkedBlockingQueue,同事回忆起不久前用线程池实现往阿里云OSS服务通过追加的方式上传文件功能,查看代码后发现问题——线程池没有关闭。为了使文件片段保存不存在错乱,每次保存文件时,都new了一个线程池对象,
ThreadPoolExecutor saveImgThreadPool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
但处理完后, 没有关闭这个线程池对象,这样线程池仍会通过take方法去取等待队列中是否还有未完成的线程任务,等待队列为空时将会一直等待,这样就导致大量的线程hung在这里了(基本是只要方法被调一次,就会产生一个hung住的线程),时间一长就达到系统所允许的最大限制(默认32768个),不能处理新任务,从而导致系统服务不可用。
延伸
- 线程状态为“waiting for monitor entry”:
- 意味着它 在等待进入一个临界区 ,所以它在”Entry Set“队列中等待。此时线程状态一般都是 Blocked:
- java.lang.Thread.State: BLOCKED (on object monitor)
- 线程状态为“waiting on condition”:
- 说明它在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了 sleep(N)。此时线程状态大致为以下几种:
- java.lang.Thread.State: WAITING (parking):一直等那个条件发生(本文案例即为此种场景);java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。
- 如果大量线程在“waiting for monitor entry”:可能是一个全局锁阻塞住了大量线程。如果短时间内打印的thread dump 文件反映,随着时间流逝,waiting for monitor entry 的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区。
- 如果大量线程在“waiting on condition”:可能是它们又跑去获取第三方资源,尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态。所以如果你发现有大量的线程都处在 Wait on condition,从线程堆栈看,正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行。也可能是如本文所提到的,由于程序编写不当所致。
参考: https://www.cnblogs.com/rainy-shurun/p/5732341.html
我的个人博客地址:http://blog.jboost.cn
我的头条空间: https://www.toutiao.com/c/user/5833678517/#mid=1636101215791112
我的github地址:https://github.com/ronwxy
我的微信公众号:jboost-ksxy
——————————————————————
欢迎关注我的微信公众号,及时获取最新分享
linux 使用jstack_案例解析:线程池使用不当导致的系统崩溃相关推荐
- 从源码角度解析线程池中顶层接口和抽象类
摘要:我们就来看看线程池中那些非常重要的接口和抽象类,深度分析下线程池中是如何将抽象这一思想运用的淋漓尽致的. 本文分享自华为云社区<[高并发]深度解析线程池中那些重要的顶层接口和抽象类> ...
- 一次排查Java线程数异常--线程池使用不当造成线程数升高
一次排查Java线程数异常--线程池使用不当造成线程数升高 参考文章: (1)一次排查Java线程数异常--线程池使用不当造成线程数升高 (2)https://www.cnblogs.com/etha ...
- Linux多线程实践(9) --简单线程池的设计与实现
线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...
- 深入解析线程池的使用
为什么需要线程池 目前的大多数网络服务器,包括Web服务器.Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短. 传 统多线程方案中我 ...
- hibernate 并发获取session失败 空指针_高并发之|通过ThreadPoolExecutor类的源码深度解析线程池执行任务的核心流程...
核心逻辑概述 ThreadPoolExecutor是Java线程池中最核心的类之一,它能够保证线程池按照正常的业务逻辑执行任务,并通过原子方式更新线程池每个阶段的状态. ThreadPoolExecu ...
- linux下c语言版线程池
1. 线程池原理 我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...
- Java多线程案例之线程池
文章目录 一. 线程池概述 1. 什么是线程池 2. Java标准库提供的线程池 二. 线程池的简单实现 一. 线程池概述 1. 什么是线程池 线程池和和字符串常量池, 数据库连接池一样, 都是为了提 ...
- 一次因线程池使用不当造成生产事故的排查记录与思考
美好的一天从bug结束 某日当我点开熟悉的界面,一个又一个请求失败的提示赫然出现在屏幕上,不会是昨晚上线的代码有问题吧? 吓得我急忙按F12查看了响应--"exception":& ...
- 线程池运用不当的一次线上事故
来自:IT人的职场进阶 在高并发.异步化等场景,线程池的运用可以说无处不在.线程池从本质上来讲,即通过空间换取时间,因为线程的创建和销毁都是要消耗资源和时间的,对于大量使用线程的场景,使用池化管理可以 ...
最新文章
- python3字典升序排序_Python(32)常用指引:排序指南
- 了解这3点,你也能成为出色的Java工程师!
- 【编程练习】正整数分解为几个连续自然数之和
- 恢复IE8自带的源代码查看器
- Java之开发工具(1) - Eclipse 如何设置注释的模板
- 数据结构:列表(双向链表)的了解与示例
- ListView与Adapter笔记:ZrcListView
- Oracle数据泵的使用
- 行如风 Angular初识
- 未来教育 ***java二级考试题库第二十五套错题***
- Python 思维锻炼
- 电脑配置挑选速成攻略
- 定时 监控 shell 服务宕机自动重启,并发送短信通知
- UWB定位技术原理图解
- Linux网卡流量限制
- lmp91000偏压配置求助
- 在MATLAB中采用M文件实现对Simulink中的S函数程序实现自动调参数
- 怎么配置计算机终端网络ip地址,如何配置计算机的IP地址并测试联网
- 步骤条的实现原理及AliceUI中步骤条Step的应用
- uniapp 下拉列表插件 lable问题
热门文章
- 品牌到底要不要做全渠道?且听他们怎么说……
- 日志组件logback介绍及配置使用方法
- 【ORACLE】 安装需要注意的问题(一)
- 如何解决SVN Commit failed (details follow): Access denied
- 【ssi】增删改查六操作小框架(八)
- 五种最易被老板开除的人
- 原生CSS,实现点击按钮出现交互弹窗【新手扫盲】
- 【网址收藏】Hadoop3.2.1 【 YARN 】源码分析 : ResourceLocalizationService解析
- kafka发送及消费消息示例
- 【收藏】Kubernetes(十七) 基于NFS的动态存储申请