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個),不能處理新任務,從而導致系統服務不可用。
延伸
參考: 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_案例解析:线程池使用不当导致的系统崩溃的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python和c混合编程 gil_终于搞
- 下一篇: 负数如何归一化处理_小白的图像处理入门(