天天看點

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

目錄

  • top指令
  • 對load的了解
  • load高、cpu高
  • load高、cpu低
  • load高的幾種原因總結:
  • RPC問題排查

top指令

top指令是最常見的檢視cpu和load的指令,拿我自己虛拟機上裝的ubuntu系統執行一下top指令(預設3秒刷1次,-d可指定重新整理時間):

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

做了一張表格比較詳細地解釋了每一部分的含義,其中重要屬性做了标紅加粗:

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

記憶體與SWAP輸出格式是一樣的,是以放在了一起寫。

對load的了解

可以使用man uptime指令看一下Linux對于load的解釋:

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

大緻意思就是說,系統load是處于運作狀态或者不可中斷狀态的程序的平均數(标紅部分表示被算入load的内容)。一個處于運作狀态的程序表示正在使用cpu或者等待使用cpu,一個不可中斷狀态的程序表示正在等待IO,例如磁盤IO或網絡IO。load的平均值通過3個時間間隔來展示,就是我們看到的1分鐘、5分鐘、15分鐘,load值和cpu核數有關,單核cpu的load=1表示系統一直處在負載狀态,但是4核cpu的load=1表示系統有75%的空閑。

load這個值,它和請求數沒有任何關系,真正和load相關的是工作線程數量,main線程是工作線程、Timer是工作線程、GC線程也是工作線程,load是以線程/程序作為統計名額,無論請求數是多少,最終都需要線程去處理,而工作線程的處理性能直接決定了最終的load值。

load高、cpu高

在一個Java應用中,排查cpu高的思路通常比較簡單,有比較固定的做法:

  • ps -ef | grep java、top指令、JPS -lmv,查詢Java應用的程序pid
  • top -H -p pid,查詢占用cpu最高的線程pid
  • 将10進制的線程pid轉成16進制的線程pid,printf “%x\n” tid 例如2000=0x7d0
  • jstack 程序pid | grep -A 20 ‘0x7d0’,查找nid比對的線程,檢視堆棧,定位引起高cpu的原因(-A表示after 20表示20行,也就是列印之後的20行堆棧資訊)

實際排查問題的時候jstack建議列印5次至少3次,根據多次的堆棧内容,再結合相關代碼段進行分析,定位高cpu出現的原因,高cpu可能是代碼段中某個bug導緻的而不是堆棧列印出來的那幾行導緻的。

例如:

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

此處的代碼問題在于:如果paramMap不為空,但dateParamMap為空的,那麼程式就會出現死循環。真是一個大BUG啊!

修改為以下代碼:

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

cpu高的情況還有一種可能的原因:頻繁FullGC,GC線程活動頻繁

針對FullGC的問題,排查思路通常為:

  • ps -ef | grep java、top指令、JPS -lmv,查詢Java應用的程序pid
  • jstat -gcutil pid 1000 1000,每隔1秒列印一次記憶體情況共列印1000次,觀察老年代(O)、MetaSpace(MU)的記憶體使用率與FullGC次數
  • 确認有頻繁的FullGC的發生,檢視GC日志,每個應用GC日志配置的路徑不同(添加參數 -XX:+PrintGCDetails)
  • jmap -dump:format=b,file=filename pid,保留現場(也可以用-XX:+HeapDumpOnOutOfMemoryError、 -XX:+HeapDumpBeforeFullGC參數)
  • 重新開機應用,迅速止血,避免引起更大的線上問題
  • dump出來的内容,結合MAT分析工具分析記憶體情況,排查FullGC出現的原因

例如:

系統上線不久後,頻繁出現記憶體溢出異常,調大XMX參數,重新開機應用後還是不起作用。于是用-XX:+HeapDumpOnOutOfMemoryError參數導出堆轉儲檔案,并用Memory Analyzer工具(MAT)分析,通過可視化界面的記憶體餅狀圖發現記憶體大量聚集在一個map對象上,進一步檢視map的引用鍊關系,發現map引用了大量的bitmap圖檔對象,分析代碼發現,這些圖檔主要用作系統中文檔單據的簽名圖檔,系統在啟動時将大量的圖檔放入到map中作為緩存使用,由于沒有緩存淘汰機制,這些圖檔一直占用這大量的記憶體,最後出現記憶體溢出異常。

MAT工具分析如下:

線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查
線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查
線上問題排查top指令對load的了解load高、cpu高load高、cpu低load高的幾種原因總結:RPC問題排查

解決:用軟引用SoftReference與圖檔對象關聯,将軟引用放入緩存中。記憶體不足時,虛拟機自動回收軟引用所關聯的對象,實作了緩存淘汰。由于緩存失效,是以再次通路時需要從磁盤上加載圖檔到緩存。

//首先定義一個HashMap,儲存軟引用對象。
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();

//再來定義一個方法,儲存Bitmap的軟引用到HashMap。
public void addBitmapToCache(String path) {
		// 強引用的Bitmap對象
        Bitmap bitmap = BitmapFactory.decodeFile(path);

        // 軟引用的Bitmap對象
        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);

        // 添加該對象到Map中使其緩存
        imageCache.put(path, softBitmap);
}
    
    //擷取的時候,可以通過SoftReference的get()方法得到Bitmap對象。
    public Bitmap getBitmapByPath(String path) {
		// 從緩存中取軟引用的Bitmap對象
        SoftReference<Bitmap> softBitmap = imageCache.get(path);

        // 判斷是否存在軟引用
        if (softBitmap == null) {
        	return null;
        }

        // 取出Bitmap對象,如果由于記憶體不足Bitmap被回收,将取得
        Bitmap bitmap = softBitmap.get();
		return bitmap;
}
           

軟引用:隻有記憶體不足時,(軟引用關聯的對象)才會被回收

弱引用:隻要發生gc,(軟引用關聯的對象)就會被回收,對象生命周期更短

軟引用,弱引用使用場景對比:

  • 如果隻是想避免OutOfMemory異常的發生,則可以使用軟引用。如果對于應用的性能更在意,想盡快回收一些占用記憶體比較大的對象,則可以使用弱引用。
  • 也可以根據對象是否經常使用來判斷。如果該對象可能會經常使用的,就盡量用軟引用。如果該對象不被使用的可能性更大些,就可以用弱引用。
  • 另外,和弱引用功能類似的是WeakHashMap。WeakHashMap對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的回收,回收以後,其條目從映射中有效地移除。WeakHashMap使用ReferenceQueue實作的這種機制。使用場景參見https://blog.csdn.net/kaka0509/article/details/73459419

load高、cpu低

在cpu不高的情況下假如load高,大機率IO高才是罪魁禍首

磁盤IO(少數情況):多線程都在讀取本地一個超大的檔案到記憶體

網絡IO:

  • 從資料庫中擷取資料(慢查詢sql)
  • 從Redis中擷取資料(操作bigkey)
  • dubbo等RPC調用響應時間長(具體見後續分析)

load高的幾種原因總結:

• 死循環或者不合理的大量循環操作,如果不是循環操作,按照現代cpu的處理速度來說處理一大段代碼也就一會會兒的事,基本對能力無消耗

• 頻繁的YoungGC

• 頻繁的FullGC

• 高磁盤IO

• 高網絡IO

RPC問題排查

  • tcpdump抓包,用wireshark分析,檢視網絡延遲是否嚴重,是否有tcp重傳
  • 檢視服務提供者cpu、load,是否飙高,列印線程堆棧,檢視有沒有死鎖、死循環、頻繁fullgc等,死鎖、死循環直接造成調用逾時,fullgc會發生停頓,造成調用耗時較長
  • 方法入參、方法傳回值是否很大,影響序列化和網絡傳輸
  • 檢視服務提供者業務邏輯是否有DB操作,有的話看是否有慢SQL
  • 檢視服務提供者業務邏輯是否有緩存操作,操作redis的bigkey
  • 檢視消費者的逾時時間設定是否合理,增加逾時時間

參考:

https://www.cnblogs.com/xrq730/p/11041741.html

https://blog.csdn.net/puhaiyang/article/details/78663942

https://www.cnblogs.com/dolphin0520/p/3784171.html

https://blog.csdn.net/arui319/article/details/8489451

https://blog.csdn.net/kaka0509/article/details/73459419