常見性能問題


一、內存泄漏

1、堆內存溢出

現象:

  (1)壓測執行一段時間后,系統處理能力下降。這時用JConsole、JVisualVM等工具連上服務器查看GC情況,每次GC回收都不徹底並且可用堆內存越來越少。

  (2)壓測持續下去,最終在日志中有報錯信息:java.lang.OutOfMemoryError.Java heap space。

排查手段:

  (1)使用jmap -histo pid > test.txt命令將堆內存使用情況保存到test.txt文件中,打開文件查看排在前50的類中有沒有熟悉的或者是公司標注的類名,如果有則高度懷疑內存泄漏是這個類導致的。

  (2)如果沒有,則使用命令:jmap -dump:live,format=b,file=test.dump pid生成test.dump文件,然后使用MAT進行分析。

  (3)如果懷疑是內存泄漏,也可以使用JProfiler連上服務器在開始跑壓測,運行一段時間后點擊“Mark Current Values”,后續的運行就會顯示增量,這時執行一下GC,觀察哪個類沒有徹底回收,基本就可以判斷是這個類導致的內存泄漏。

解決方式:優化代碼,對象使用完畢,需要置成null。

2、持久代溢出

現象:壓測執行一段時間后,日志中有報錯信息:java.lang.OutOfMemoryError: PermGen space。

產生原因:由於類、方法描述、字段描述、常量池、訪問修飾符等一些靜態變量太多,將持久代占滿導致持久代溢出。

解決方法:修改JVM參數,將XX:MaxPermSize參數調大。盡量減少靜態變量。

3、棧內存溢出

現象:壓測執行一段時間后,日志中有報錯信息:java.lang.StackOverflowError。

產生原因:線程請求的棧深度大於虛擬機所允許的最大深度,遞歸沒返回,戒者循環調用造成。

解決方法:修改JVM參數,將Xss參數改大,增加棧內存。棧內存溢出一定是做批量操作引起的,減少批處理數據量。

4、系統內存溢出

現象:壓測執行一段時間后,日志中有報錯信息:java.lang.OutOfMemoryError: unable to create new native thread。

產生原因:操作系統沒有足夠的資源來產生返個線程造成的。系統創建線程時,除了要在Java堆中分配內存外,操作系統本身也需要分配資源來創建線程。因此,當線程數量大到一定程度以后,堆中或許還有空間,但是操作系統分配不出資源來了,就出現這個異常了。

解決方法:

  (1)減少堆內存

  (2)減少線程數量

  (3)如果線程數量不能減少,則減少每個線程的堆棧大小,通過-Xss減小單個線程大小,以便能生產更多的線程。

5、JAVA直接內存溢出

現象:壓測執行一段時間后,日志中有報錯信息:OutOfMemoryError

產生原因:

  (1)直接內存大多時候也被稱為堆外內存,直接內存通過 native 方法可以分配堆外內存,通過 DirectByteBuffer 對象來操作。直接內存不屬於 Java 堆,所以它不受堆內存大小限制,但是它受物理內存大小的限制。

  (2)可以通過 -XX:MaxDirectMemorySize 參數來設置最大可用直接內存,如果啟動時未設置則默認為最大堆內存大小,即與 -Xmx 相同。即假如最大堆內存為1G,則默認直接內存也為1G,那么 JVM 最大需要的內存大小為2G多一些。當直接內存達到最大限制時就會觸發GC,如果回收失敗則會引起OutOfMemoryError。

  (3)直接內存在讀和寫的性能都優於堆內內存,但是內存申請速度卻不如堆內內存。

解決方法:因此直接內存適用於需要大內存空間且頻繁訪問的場合,不適用於頻繁申請釋放內存的場合。在需要頻繁申請的場景下不應該使用直接內存(DirectMemory),而應該使用堆內內存(HeapMemory)。

二、CPU過高

1、us cpu高

現象:壓測過程中,使用top命令查看系統資源占用情況,us cpu過高,超過50%以上。

排查手段:

  (1)使用top命令是哪個進程消耗CPU高

  (2)再找到CPU消耗高的線程:top -H -p 進程號

  (3)把線程號轉換成16進制:printf "%x\n" 線程號

  (4)再用jstack命令分析這個線程是在干什么:jstack 進程號 | grep 16進制的線程號

  (5)通過JProfiler的CPU Views視圖的層層分析,可以清楚的找到造成CPU高的原因

2、sy cpu高

現象:壓測過程中,使用top命令查看系統資源占用情況,sy cpu過高,超過50%以上。

排查手段:

  (1)首先查看磁盤繁忙程度、磁盤的隊列(iostat、nmon)

  (2)如果磁盤沒有問題,則使用strace查看系統內核調用情況

三、線程死鎖

現象:

  (1)壓測進行一段時間后,程序停頓,報超時錯誤。但這種現象並不一定就是線程死鎖造成的,也可能是數據庫/中間件連接池被占滿、數據庫死鎖造成的。

  (2)能夠打開頁面,但獲取不到數據

排查手段:

  (1)使用jstack命令查看Java進程下所有線程的情況:jstack -l 進程號

  (2)如果有Blocked狀態的線程,說明有線程死鎖的狀況。如果大量線程都是Waiting狀態,則需要去關注數據庫和中間件,可能會有排隊情況。

  (3)也可以使用JConsole、JVisualVM及JProfiler等工具直接查看所有線程的情況

四、數據庫連接池不釋放

現象:壓測進行一段時間后,報連接超時的錯誤。

排查手段:

  (1)去數據庫查看應用程序到數據庫的連接有多少個:show full processlist。加入應用程序中配置的最大連接數為30,而通過命令show full processlist查看到的從應用服務器連接過來的連接數也為30,證明數據庫連接池占滿了。

  (2)將應用程序中的最大連接數改大一點(比如100),再重新進行壓測,如果還是出現連接池被占滿的情況,則證明是數據庫連接池不釋放造成的.

解決方法:排查代碼,數據庫連接部分應該是有創建連接但是沒有關閉連接的情況。讓開發修改代碼即可。

五、數據庫死鎖

現象:

  (1)壓測進行一段時間后,報連接超時的錯誤。

  (2)程序在執行的過程中,點擊確定或保存按鈕,程序沒有響應,也沒有出現報錯。

排查手段:查看數據庫日志,看有沒有死鎖的情況:show engine innodb status\G。

六、SQL使用不合理

現象:事物響應時間慢

排查手段:

  (1)打開數據庫的慢查詢

  (2)通過命令找到執行比較久的SQL語句:mysqldumpslow

  (3)利用explain來優化這條SQL語句

七、TPS上不去

1、網絡帶寬

  在壓力測試中,有時候要模擬大量的用戶請求,如果單位時間內傳遞的數據包過大,超過了帶寬的傳輸能力,那么就會造成網絡資源競爭,間接導致服務端接收到的請求數達不到服務端的處理能力上限。

2、連接池

  最大連接數太少,造成請求等待。連接池一般分為服務器中間件連接池(比如Tomcat)和數據庫連接池(或者理解為最大允許連接數也行)。

3、垃圾回收機制

  從常見的應用服務器來說,比如Tomcat,如果堆內存設置比較小,就會造成新生代的Eden區頻繁的進行Young GC,老年代的Full GC也回收較頻繁,那么對TPS也是有一定影響的,因為垃圾回收時通常會暫停所有線程的工作。

4、數據庫

  高並發情況下,如果請求數據需要寫入數據庫,且需要寫入多個表的時候,如果數據庫的最大連接數不夠,或者寫入數據的SQL沒有索引沒有綁定變量,抑或沒有主從分離、讀寫分離等,就會導致數據庫事務處理過慢,影響到TPS。

5、硬件資源

包括CPU(配置、使用率等)、內存(占用率等)、磁盤(I/O、頁交換等)。

6、壓力機

  比如Jmeter和Loadrunner,單機負載能力有限,如果需要模擬的用戶請求數超過其負載極限,也會間接影響TPS(這個時候就需要進行分布式壓測來解決其單機負載的問題)。

7、業務邏輯

業務解耦度較低,較為復雜,整個事務處理線被拉長也會導致TPS上不去。

8、系統架構

比如是否有緩存服務,緩存服務器配置,緩存命中率、緩存穿透以及緩存過期等,都會影響到測試結果。

八、500或503錯誤

現象:壓測過程中,服務器返回500或503錯誤

排查手段

  (1)通過瀏覽器訪問網站,如果瀏覽器打不開,服務器可能掛了。可能的原因是:內存溢出、數據庫連接池滿了、線程死鎖。

  (2)先看JVM內存是否滿了:jstat -gcutil 2384 1000 5

  (3)看數據庫連接池大小是否占滿,把最大連接數改大,如果還是出現這種現象,證明是數據庫連接池不釋放。

  (4)最后看是否有線程死鎖:jstack -l 進程號

九、性能問題分析流程

  1、查看服務器的CPU、內存 、負載等情況,包括應用服務器和數據庫服務器

  2、查看數據庫健康狀態,數據庫死鎖、連接池不釋放

  3、查看項目日志(查看無報錯現象)

  4、查看jvm的gc等情況


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM