記幾回JAVA系統故障問題定位過程

把本身之前碰到的case彙總列下,做爲對本身過去的一部分工做總結。php

問題定位通常步驟

  1. 具有常見的理論知識,不必定要全記住細節。可是須要知道問題的關聯性,而後根據某些關鍵字搜索或者查閱資料等。
  2. 溝通故障現象,根據故障的嚴重性決定是重啓規避仍是在現場直接定位。是集羣,單機,某個業務系統,抑或某個業務模塊發生問題。
  3. 查看系統運行狀況,好比應用日誌,jvm內存,線程,操做系統的等狀況。
    1. 操做系統狀況(CPU,內存,磁盤,網絡)
    2. JVM內存,線程,GC日誌
    3. 業務系統日誌
  4. 針對性分析。

案例分享

內存溢出

現象和緣由

這類故障的典型現象是系統無響應,功能基本所有癱瘓。常見緣由包括:html

  • 查詢數據庫結果集過大,沒有進行分頁處理。
  • 過大的緩存對象佔用了大量的內存空間
  • 或者是頻繁建立了大對象,致使JVM新生代和老年代處於100%,系統頻繁FullGC。
  • 堆外內存泄露,能夠參考perftools查看堆外內存泄露

定位方法

定位這一類問題的神器是使用MAT工具。 介紹MAT的文章不少了,入門的能夠看看這篇。有幾個小細節,須要說下:java

  • 最容易被忽略的就是忘記打開「keep unreachable objects」 參數了。有時候,dump下來的文件很大,可是mat分析時,卻顯示總體佔用內存很小時,就須要打開這個參數。
  • 有時候幸運的話,可以根據方法鏈或者類名稱就能直接看到哪些對象佔用過大。若是碰到map,則須要看到裏面的屬性,好比根據map的key值在代碼中全文搜索。
  • 有時候,jmap出現bug或者沒法直接經過jmap命令來導出dump時,可使用gcore <pid>命令。
  • 有時kill -3沒有設置好或者jstack 出現bug時,也能夠在mat裏面查看線程堆棧狀況,只是看起來比較痛苦。
  • 在micro benchmark時,能夠查看下容器的填充率。
  • 固然,既然須要分析內存佔用狀況,你有時候確定想看看java對象大小到底是怎麼計算的。這個後文有介紹。。

併發問題

現象和緣由

這類問題現象比較複雜,筆者嘗試進行總結下。常見的現象是git

  • 業務系統無響應或者響應緩慢,CPU使用率100%(在多核狀況下,能夠經過 top 1看到各核使用的狀況,一般能夠看到某核)。
  • 單線程狀況下,業務操做沒問題,併發量高的時候就數據紊亂,會報校驗出錯,數據結果不對。
  • 出現Resource unavailable,有時候沒法ssh到服務器上。

常見緣由以下:程序員

  • 出現了死循環
    • 無限遞歸
    • HashMap死循環
  • 使用有狀態服務,併發狀況下數據紊亂
  • 線程過多,沒有限制個數。

定位方法

在真正進行解決問題前,若是對多線程不清楚的話,能夠先看看筆者的《JAVA學習筆記之多線程編程》系列文章。github

定位線程問題也有一個神器,就是IBM Thread and Monitor Dump Analyzer for Java。通常線程數,死鎖,鎖爭用狀況可以盡收眼底。web

本工具使用起來比較方便,可是須要對線程狀態有個基本認識:算法

  • RUNNABLE,處於執行狀態。
  • BLOCKED,處於受阻塞並等待某個監視器鎖狀態。
  • WAITING,處於無限期地等待另外一個線程來執行某一特定操做狀態。

RUNNABLE處於佔有鎖狀態,BLOCKED處於等待獲取鎖狀態,WAITING處於被別人通知狀態。也就是說,若是某個core使用率處於CPU100%的話,一般只須要關注RUNNABLE狀態。數據庫

典型的業務案例

因爲多線程編程的確比較複雜,這個有必要舉幾個案例,大概說一下。編程

  • HashMap死循環。某業務代碼,一個類內部使用HashMap來封裝動態變量。剛開始,是單線程執行。後來,爲了提高運行性能,把某段邏輯改爲了異步。而後,有一天終於被坑了。
  • HashMap死循環。某業務代碼,自行封裝hibernate框架。而後在系統啓動時,時不時發現系統啓動假死。經過經過查看CPU使用率以及線程堆棧,發現存在多個線程同時加載hibernate,而後觸發hibernate的annatation組件加載。而這個annatation組件內部使用了HashMap,從而發生hashMap死循環。解決方法經過引入static holder單例模式,確保只有一個線程在初始化hibernate框架。
  • 有狀態服務。某RPC通訊框架,直接反射反射建立實例。後來聽說爲了提高反射性能,把反射後的實例該緩存了,這個至關於這個類變成單例了,可是業務方並無遵照「無狀態服務」這個約定。而後,有一天終於被坑了。
  • 線程過多。某業務邏輯,直接for循環建立大量線程,jstack後發現建立了好幾k個線程。

TPS上不去

現象和緣由

通常在業務系統壓力測試時,隨着壓力的上升,系統的吞吐量逐漸增長。直到到達拐點後,系統的吞吐量不升反降。

從緣由上分析,實在太多。有宏觀上的,好比架構問題;有細節上的,好比算法實現問題。筆者知道這個話題太大了,只能根據本身的經驗來談幾句。

定位方法

從以己及彼這個角度來看,大概能夠從以下3個方面入手:

  • JVM代碼執行效率:
    • JVM啓動參數,垃圾回收算法(響應優先仍是吞吐量優先)
    • 結合jProfiler,VisualVM工具,看看hotspot代碼,方法是否重複調用了屢次,方法執行時間過長,避免頻繁建立沒必要要的對象,充分利用操做系統的預讀後寫機制,儘可能作到順序寫和順序讀,充分使用零拷貝機制,注意到僞共享問題,減小沒必要要的系統調用。
    • 結合MAT工具,查看是否存在大對象
    • 結合JCA工具,分析線程狀況,是否存在大量線程等待鎖狀況,控制鎖的粒度
  • 計算機體系結構:
    • 硬件:CPU頻率、幾core、緩存大小,內存大小,磁盤、SSD讀寫速度
    • 操做系統:版本, 典型參數設置如句柄數,內存頁大小,注意cpu us、sys百分比
    • 網絡:結合rtt和bandwidth把網卡跑滿,詳細的能夠參考這個
  • 系統架構
    • SOA框架:提供分佈式計算能力,把原來須要自身計算的部分轉移到其餘機器上
    • MQ:把沒必要要同步的計算異步化,隊列化。
    • 緩存:是否沒有緩存或者緩存策略不合理,對頻繁變化的數據進行緩存
    • 存儲:是否沒走索引,或者索引設置不夠合理;表結構設計不合理,沒有適當冗餘

性能優化涉及的知識太博大精深了,筆者僅僅是把一般須要考慮的因素列在這裏。在JAVA性能優化方面,有2本經典的書供參考,就是《Effective JAVA》和《JAVA Performance》。

在優化的過程當中,首先仍是須要找到關鍵路徑,迅速解決問題,這個對公司很重要。另外一方面,從我的發展角度來看,須要多作些micro benchmark積累經驗值。

另外,性能優化非常對一我的綜合素質的考驗,須要不少方面的知識。好比對操做系統,CPU,各類算法數據結構。這方面筆者也在持續地學習中...


非主流bug

這個世界上沒有徹底正確的程序,由於其沒法被證實。一部分bug是由於缺少必要的校驗,還有一部分是缺少對周邊環境的瞭解。簡單列幾個讓本身印象深入的把。

  • 對環境不熟
    • 把文件寫在jboss的class目錄下面,致使jboss自動加載,進而致使系統無響應。通過一番定位無功而返。最後仔細看了下應用日誌,發現jboss在自動重啓。最後把自動重啓參數調整了從而解決此問題。
  • 對jvm不熟
    • 某業務系統,須要支持動態webservice加載。後來發現每次修改後,都沒法生效。後來仍是經過自定義classloader解決。
  • 對api不熟
    • drools api誤用。裏面的contains不是針對字符串,而是針對集合類。這個bug定位了不少天,剛開始覺得是併發bug,由於前10次能夠正常調用,可是後面的調用通通失敗。後來,發現就這個contains有問題。再後來,把doc翻翻,用錯了。
    • 網絡編程時,沒有設置合理的connection timeout 和 soTimeout值,當對方系統或者網絡鏈接不可用時,致使佔用大量線程。另外, 注意到Socket.setSoLinger(boolean on, int linger)這個linger參數單位是秒,在API單位誤解形成的嚴重故障中有詳細介紹。

附錄

JAVA對象大小

這個在R大的Java 程序的編譯,加載 和 執行裏的P.124有詳細介紹。

另外,多說一句,Oracle在Java 1.7.0_06版本里,更改String類中的字符串內部表示。詳情點擊這裏

常見時延大小

Latency Comparison Numbers
--------------------------
L1 cache reference                            0.5 ns
Branch mispredict                             5   ns
L2 cache reference                            7   ns             14x L1 cache
Mutex lock/unlock                            25   ns
Main memory reference                       100   ns             20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy              3,000   ns
Send 1K bytes over 1 Gbps network        10,000   ns    0.01 ms
Read 4K randomly from SSD*              150,000   ns    0.15 ms
Read 1 MB sequentially from memory      250,000   ns    0.25 ms
Round trip within same datacenter       500,000   ns    0.5  ms
Read 1 MB sequentially from SSD*      1,000,000   ns    1    ms  4X memory
Disk seek                            10,000,000   ns   10    ms  20x datacenter roundtrip
Read 1 MB sequentially from disk     20,000,000   ns   20    ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA     150,000,000   ns  150    ms

第一次看到這個數據時,網絡速度竟然大於磁盤隨機讀寫速度,這個着實讓我印象深入。

各類API性能以及性能優化技巧

文檔 各類API性能_性能優化技巧

視頻 各類API性能_性能優化技巧

參考

Latency Numbers Every Programmer Should Know

Java程序員也應該知道的一些網絡知識

本文完,歡迎指正,討論和分享 :)

相關文章
相關標籤/搜索