把本身之前碰到的case彙總列下,做爲對本身過去的一部分工做總結。php
問題定位通常步驟
- 具有常見的理論知識,不必定要全記住細節。可是須要知道問題的關聯性,而後根據某些關鍵字搜索或者查閱資料等。
- 溝通故障現象,根據故障的嚴重性決定是重啓規避仍是在現場直接定位。是集羣,單機,某個業務系統,抑或某個業務模塊發生問題。
- 查看系統運行狀況,好比應用日誌,jvm內存,線程,操做系統的等狀況。
- 操做系統狀況(CPU,內存,磁盤,網絡)
- JVM內存,線程,GC日誌
- 業務系統日誌
- 針對性分析。
案例分享
內存溢出
現象和緣由
這類故障的典型現象是系統無響應,功能基本所有癱瘓。常見緣由包括: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到服務器上。
常見緣由以下:程序員
- 出現了死循環
- 使用有狀態服務,併發狀況下數據紊亂
- 線程過多,沒有限制個數。
定位方法
在真正進行解決問題前,若是對多線程不清楚的話,能夠先看看筆者的《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程序員也應該知道的一些網絡知識
本文完,歡迎指正,討論和分享 :)