摘要:直到有一天你會碰到線上奇奇怪怪的問題,如線程執行一個任務遲遲沒有返回,應用假死。正好這次借助之前的一次生產問題來聊聊如何排查和解決問題。本地模擬上文介紹的是線程相關問題,現在來分析下內存的問題。盡可能的減少多線程競爭鎖。
前言
之前或多或少分享過一些內存模型、對象創建之類的內容,其實大部分人看完都是懵懵懂懂,也不知道這些的實際意義。
直到有一天你會碰到線上奇奇怪怪的問題,如:
線程執行一個任務遲遲沒有返回,應用假死。
接口響應緩慢,甚至請求超時。
CPU 高負載運行。
這類問題并不像一個空指針、數組越界這樣明顯好查,這時就需要剛才提到的內存模型、對象創建、線程等相關知識結合在一起來排查問題了。
正好這次借助之前的一次生產問題來聊聊如何排查和解決問題。
生產現象首先看看問題的背景吧:
我這其實是一個定時任務,在固定的時間會開啟 N 個線程并發的從 Redis 中獲取數據進行運算。
業務邏輯非常簡單,但應用一般涉及到多線程之后再簡單的事情都要小心對待。
果不其然這次就出問題了。
現象:原本只需要執行幾分鐘的任務執行了幾個小時都沒退出。翻遍了所有的日志都沒找到異常。
于是便開始定位問題之路。
定位問題既然沒辦法直接從日志中發現異常,那就只能看看應用到底在干嘛了。
最常見的工具就是 JDK 自帶的那一套。
這次我使用了 jstack 來查看線程的執行情況,它的作用其實就是 dump 當前的線程堆棧。
當然在 dump 之前是需要知道我應用的 pid 的,可以使用 jps -v 這樣的方式列出所有的 Java 進程。
當然如果知道關鍵字的話直接使用 ps aux|grep java 也是可以的。
拿到 pid=1523 了之后就可以利用 jstack 1523 > 1523.log 這樣的方式將 dump 文件輸出到日志文件中。
如果應用簡單不復雜,線程這些也比較少其實可以直接打開查看。
但復雜的應用導出來的日志文件也比較大還是建議用專業的分析工具。
我這里的日志比較少直接打開就可以了。
因為我清楚知道應用中開啟的線程名稱,所以直接根據線程名就可以在日志中找到相關的堆棧:
所以通常建議大家線程名字給的有意義,在排查問題時很有必要。
其實其他幾個線程都和這里的堆棧類似,很明顯的看出都是在做 Redis 連接。
于是我登錄 Redis 查看了當前的連接數,發現已經非常高了。
這樣 Redis 的響應自然也就變慢了。
接著利用 jps -v 列出了當前所以在跑的 Java 進程,果不其然有好幾個應用都在查詢 Redis,而且都是并發連接,問題自然就找到了。
解決辦法所以問題的主要原因是:大量的應用并發查詢 Redis,導致 Redis 的性能降低。
既然找到了問題,那如何解決呢?
減少同時查詢 Redis 的應用,分開時段降低 Redis 的壓力。
將 Redis 復制幾個集群,各個應用分開查詢。但是這樣會涉及到數據的同步等運維操作,或者由程序了進行同步也會增加復雜度。
目前我們選擇的是第一個方案,效果很明顯。
本地模擬上文介紹的是線程相關問題,現在來分析下內存的問題。
以這個類為例:
https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/oom/heap/HeapOOM.java
public class HeapOOM { public static void main(String[] args) { Listlist = new ArrayList<>(10) ; while (true){ list.add("1") ; } } }
啟動參數如下:
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/xx/Documents
為了更快的突出內存問題將堆的最大內存固定在 20M,同時在 JVM 出現 OOM 的時候自動 dump 內存到 /Users/xx/Documents(不配路徑則會生成在當前目錄)。
執行之后果不其然出現了異常:
同時對應的內存 dump 文件也生成了。
內存分析這時就需要相應的工具進行分析了,最常用的自然就是 MAT 了。
我試了一個在線工具也不錯(文件大了就不適合了):
http://heaphero.io/index.jsp
上傳剛才生成的內存文件之后:
因為是內存溢出,所以主要觀察下大對象:
也有相應提示,這個很有可能就是內存溢出的對象,點進去之后:
看到這個堆棧其實就很明顯了:
在向 ArrayList 中不停的寫入數據時,會導致頻繁的擴容也就是數組復制這些過程,最終達到 20M 的上限導致內存溢出了。
更多建議上文說過,一旦使用了多線程,那就要格外小心。
以下是一些日常建議:
盡量不要在線程中做大量耗時的網絡操作,如查詢數據庫(可以的話在一開始就將數據從從 DB 中查出準備好)。
盡可能的減少多線程競爭鎖。可以將數據分段,各個線程分別讀取。
多利用 CAS+自旋 的方式更新數據,減少鎖的使用。
應用中加上 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp 參數,在內存溢出時至少可以拿到內存日志。
線程池監控。如線程池大小、隊列大小、最大線程數等數據,可提前做好預估。
JVM 監控,可以看到堆內存的漲幅趨勢,GC 曲線等數據,也可以提前做好準備。
總結線上問題定位需要綜合技能,所以是需要一些基礎技能。如線程、內存模型、Linux 等。
當然這些問題沒有實操過都是紙上談兵;如果第一次碰到線上問題,不要慌張,反而應該慶幸解決之后你又會習得一項技能。
號外最近在總結一些 Java 相關的知識點,感興趣的朋友可以一起維護。
地址: https://github.com/crossoverJie/Java-Interview
歡迎關注公眾號一起交流:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71531.html
摘要:排查異常日志,發現沒有該問題存在。測試功能正常,沒有重現線上問題。解決問題原因定位好了,剩下的就是如何解決了。兩個方案修改線上配置該上實施難度系數高,因為公司使用的統一發布部署平臺,開發人員無服務器操作權限。 問題 XX系統中,一個用戶需要維護的項目數過多,填寫的任務數超多,產生了一次工時保存中,只有前面一部分的xx數據持久化到數據庫,后面的數據沒有保存。 圖1 showImg(htt...
摘要:排查異常日志,發現沒有該問題存在。測試功能正常,沒有重現線上問題。解決問題原因定位好了,剩下的就是如何解決了。兩個方案修改線上配置該上實施難度系數高,因為公司使用的統一發布部署平臺,開發人員無服務器操作權限。 問題 XX系統中,一個用戶需要維護的項目數過多,填寫的任務數超多,產生了一次工時保存中,只有前面一部分的xx數據持久化到數據庫,后面的數據沒有保存。 圖1 showImg(htt...
摘要:排查異常日志,發現沒有該問題存在。測試功能正常,沒有重現線上問題。解決問題原因定位好了,剩下的就是如何解決了。兩個方案修改線上配置該上實施難度系數高,因為公司使用的統一發布部署平臺,開發人員無服務器操作權限。 問題 XX系統中,一個用戶需要維護的項目數過多,填寫的任務數超多,產生了一次工時保存中,只有前面一部分的xx數據持久化到數據庫,后面的數據沒有保存。 圖1 showImg(htt...
摘要:近日在工作中由于疏忽問題導致某個客戶的系統直接崩盤,極大的影響了用戶使用產品的體驗。在經過修改之后,不得不思考下在日常開發中的一些壞習慣以及如何規避這些日常問題了。同時由于我們未能對錯誤進行好的處理,導致程序直接卡死。 近日在工作中由于疏忽問題導致某個客戶的系統直接崩盤,極大的影響了用戶使用產品的體驗。在經過修改之后,不得不思考下在日常開發中的一些壞習慣以及如何規避這些日常問題了。 在...
摘要:直接顯示了一個疑似內存泄漏的問題。然后分析文件給出的信息,發現一個叫的類。文件里面說的內存泄漏的大概的意思就是說,這個類里面的存放的東西太多了,爆掉了。修改了代碼將調用的地方改成了單例。修改完線上跑了一段日子,后來也沒有出現過這樣的問題。 問題描述: ????早上去公司上班,突然就郵件一直報警,接口報異常,然后去查服務器的運行情況,發現java的cpu爆了.接著就開始排查問題 問題解決...
閱讀 3026·2021-11-24 09:39
閱讀 2255·2021-10-08 10:05
閱讀 2749·2021-09-24 13:52
閱讀 1569·2021-09-22 15:07
閱讀 589·2019-08-30 15:55
閱讀 1808·2019-08-30 15:53
閱讀 687·2019-08-30 15:44
閱讀 3116·2019-08-30 11:20