一俊犯、內(nèi)存泄漏
1、堆內(nèi)存溢出
現(xiàn)象:
∠凸摺(1)壓測執(zhí)行一段時(shí)間后,系統(tǒng)處理能力下降棒掠。這時(shí)用JConsole孵构、JVisualVM等工具連上服務(wù)器查看GC情況,每次GC回收都不徹底并且可用堆內(nèi)存越來越少烟很。
【笔(2)壓測持續(xù)下去,最終在日志中有報(bào)錯(cuò)信息:java.lang.OutOfMemoryError.Java heap space雾袱。
排查手段:
⌒羯浮(1)使用jmap -histo pid > test.txt命令將堆內(nèi)存使用情況保存到test.txt文件中,打開文件查看排在前50的類中有沒有熟悉的或者是公司標(biāo)注的類名芹橡,如果有則高度懷疑內(nèi)存泄漏是這個(gè)類導(dǎo)致的毒坛。
(2)如果沒有,則使用命令:jmap -dump:live,format=b,file=test.dump pid生成test.dump文件煎殷,然后使用MAT進(jìn)行分析屯伞。
(3)如果懷疑是內(nèi)存泄漏豪直,也可以使用JProfiler連上服務(wù)器在開始跑壓測劣摇,運(yùn)行一段時(shí)間后點(diǎn)擊“Mark Current Values”,后續(xù)的運(yùn)行就會(huì)顯示增量弓乙,這時(shí)執(zhí)行一下GC末融,觀察哪個(gè)類沒有徹底回收,基本就可以判斷是這個(gè)類導(dǎo)致的內(nèi)存泄漏暇韧。
解決方式:優(yōu)化代碼勾习,對象使用完畢,需要置成null锨咙。
2语卤、持久代溢出
現(xiàn)象:壓測執(zhí)行一段時(shí)間后,日志中有報(bào)錯(cuò)信息:java.lang.OutOfMemoryError: PermGen space酪刀。
產(chǎn)生原因:由于類粹舵、方法描述、字段描述骂倘、常量池眼滤、訪問修飾符等一些靜態(tài)變量太多,將持久代占滿導(dǎo)致持久代溢出历涝。
解決方法:修改JVM參數(shù)诅需,將XX:MaxPermSize參數(shù)調(diào)大。盡量減少靜態(tài)變量荧库。
3堰塌、棧內(nèi)存溢出
現(xiàn)象:壓測執(zhí)行一段時(shí)間后,日志中有報(bào)錯(cuò)信息:java.lang.StackOverflowError分衫。
產(chǎn)生原因:線程請求的棧深度大于虛擬機(jī)所允許的最大深度场刑,遞歸沒返回,戒者循環(huán)調(diào)用造成蚪战。
解決方法:修改JVM參數(shù)牵现,將Xss參數(shù)改大,增加棧內(nèi)存邀桑。棧內(nèi)存溢出一定是做批量操作引起的瞎疼,減少批處理數(shù)據(jù)量。
4壁畸、系統(tǒng)內(nèi)存溢出
現(xiàn)象:壓測執(zhí)行一段時(shí)間后贼急,日志中有報(bào)錯(cuò)信息:java.lang.OutOfMemoryError: unable to create new native thread珠洗。
產(chǎn)生原因:操作系統(tǒng)沒有足夠的資源來產(chǎn)生返個(gè)線程造成的赶掖。系統(tǒng)創(chuàng)建線程時(shí)斯棒,除了要在Java堆中分配內(nèi)存外剿涮,操作系統(tǒng)本身也需要分配資源來創(chuàng)建線程。因此腻异,當(dāng)線程數(shù)量大到一定程度以后进副,堆中或許還有空間,但是操作系統(tǒng)分配不出資源來了悔常,就出現(xiàn)這個(gè)異常了影斑。
解決方法:
(1)減少堆內(nèi)存
』颉(2)減少線程數(shù)量
〗没А(3)如果線程數(shù)量不能減少,則減少每個(gè)線程的堆棧大小残邀,通過-Xss減小單個(gè)線程大小皆辽,以便能生產(chǎn)更多的線程。
5芥挣、JAVA直接內(nèi)存溢出
現(xiàn)象:壓測執(zhí)行一段時(shí)間后驱闷,日志中有報(bào)錯(cuò)信息:OutOfMemoryError
產(chǎn)生原因:
(1)直接內(nèi)存大多時(shí)候也被稱為堆外內(nèi)存空免,直接內(nèi)存通過 native 方法可以分配堆外內(nèi)存空另,通過 DirectByteBuffer 對象來操作。直接內(nèi)存不屬于 Java 堆蹋砚,所以它不受堆內(nèi)存大小限制扼菠,但是它受物理內(nèi)存大小的限制。
“痈馈(2)可以通過 -XX:MaxDirectMemorySize 參數(shù)來設(shè)置最大可用直接內(nèi)存循榆,如果啟動(dòng)時(shí)未設(shè)置則默認(rèn)為最大堆內(nèi)存大小,即與 -Xmx 相同墨坚。即假如最大堆內(nèi)存為1G冯痢,則默認(rèn)直接內(nèi)存也為1G,那么 JVM 最大需要的內(nèi)存大小為2G多一些框杜。當(dāng)直接內(nèi)存達(dá)到最大限制時(shí)就會(huì)觸發(fā)GC,如果回收失敗則會(huì)引起OutOfMemoryError袖肥。
∵淙琛(3)直接內(nèi)存在讀和寫的性能都優(yōu)于堆內(nèi)內(nèi)存,但是內(nèi)存申請速度卻不如堆內(nèi)內(nèi)存椎组。
解決方法:因此直接內(nèi)存適用于需要大內(nèi)存空間且頻繁訪問的場合油狂,不適用于頻繁申請釋放內(nèi)存的場合。在需要頻繁申請的場景下不應(yīng)該使用直接內(nèi)存(DirectMemory),而應(yīng)該使用堆內(nèi)內(nèi)存(HeapMemory)专筷。
二弱贼、CPU過高
1、us cpu高
現(xiàn)象:壓測過程中磷蛹,使用top命令查看系統(tǒng)資源占用情況吮旅,us cpu過高,超過50%以上味咳。
排查手段:
”硬(1)使用top命令是哪個(gè)進(jìn)程消耗CPU高
(2)再找到CPU消耗高的線程:top -H -p 進(jìn)程號(hào)
〔凼弧(3)把線程號(hào)轉(zhuǎn)換成16進(jìn)制:printf "%x\n" 線程號(hào)
≡鹑隆(4)再用jstack命令分析這個(gè)線程是在干什么:jstack 進(jìn)程號(hào) | grep 16進(jìn)制的線程號(hào)
(5)通過JProfiler的CPU Views視圖的層層分析掂铐,可以清楚的找到造成CPU高的原因
2罕拂、sy cpu高
現(xiàn)象:壓測過程中,使用top命令查看系統(tǒng)資源占用情況全陨,sy cpu過高爆班,超過50%以上。
排查手段:
】靖洹(1)首先查看磁盤繁忙程度蛋济、磁盤的隊(duì)列(iostat、nmon)
∨谝丁(2)如果磁盤沒有問題碗旅,則使用strace查看系統(tǒng)內(nèi)核調(diào)用情況
三、線程死鎖
現(xiàn)象:
【迪ぁ(1)壓測進(jìn)行一段時(shí)間后祟辟,程序停頓,報(bào)超時(shí)錯(cuò)誤侣肄。但這種現(xiàn)象并不一定就是線程死鎖造成的旧困,也可能是數(shù)據(jù)庫/中間件連接池被占滿、數(shù)據(jù)庫死鎖造成的稼锅。
『鹁摺(2)能夠打開頁面,但獲取不到數(shù)據(jù)
排查手段:
【鼐唷(1)使用jstack命令查看Java進(jìn)程下所有線程的情況:jstack -l 進(jìn)程號(hào)
∞趾小(2)如果有Blocked狀態(tài)的線程,說明有線程死鎖的狀況锥债。如果大量線程都是Waiting狀態(tài)陡蝇,則需要去關(guān)注數(shù)據(jù)庫和中間件痊臭,可能會(huì)有排隊(duì)情況。
〉欠颉(3)也可以使用JConsole广匙、JVisualVM及JProfiler等工具直接查看所有線程的情況
四、數(shù)據(jù)庫連接池不釋放
現(xiàn)象:壓測進(jìn)行一段時(shí)間后恼策,報(bào)連接超時(shí)的錯(cuò)誤鸦致。
排查手段:
(1)去數(shù)據(jù)庫查看應(yīng)用程序到數(shù)據(jù)庫的連接有多少個(gè):show full processlist戏蔑。加入應(yīng)用程序中配置的最大連接數(shù)為30蹋凝,而通過命令show full processlist查看到的從應(yīng)用服務(wù)器連接過來的連接數(shù)也為30,證明數(shù)據(jù)庫連接池占滿了总棵。
△⒓拧(2)將應(yīng)用程序中的最大連接數(shù)改大一點(diǎn)(比如100),再重新進(jìn)行壓測情龄,如果還是出現(xiàn)連接池被占滿的情況迄汛,則證明是數(shù)據(jù)庫連接池不釋放造成的.
解決方法:排查代碼,數(shù)據(jù)庫連接部分應(yīng)該是有創(chuàng)建連接但是沒有關(guān)閉連接的情況骤视。讓開發(fā)修改代碼即可鞍爱。
五、數(shù)據(jù)庫死鎖
現(xiàn)象:
∽ㄐ铩(1)壓測進(jìn)行一段時(shí)間后睹逃,報(bào)連接超時(shí)的錯(cuò)誤。
〉豢稀(2)程序在執(zhí)行的過程中沉填,點(diǎn)擊確定或保存按鈕,程序沒有響應(yīng)佑笋,也沒有出現(xiàn)報(bào)錯(cuò)翼闹。
排查手段:查看數(shù)據(jù)庫日志,看有沒有死鎖的情況:show engine innodb status\G蒋纬。
六猎荠、SQL使用不合理
現(xiàn)象:事物響應(yīng)時(shí)間慢
排查手段:
(1)打開數(shù)據(jù)庫的慢查詢
(2)通過命令找到執(zhí)行比較久的SQL語句:mysqldumpslow
∈癖浮(3)利用explain來優(yōu)化這條SQL語句
七关摇、TPS上不去
1、網(wǎng)絡(luò)帶寬
在壓力測試中碾阁,有時(shí)候要模擬大量的用戶請求拒垃,如果單位時(shí)間內(nèi)傳遞的數(shù)據(jù)包過大,超過了帶寬的傳輸能力瓷蛙,那么就會(huì)造成網(wǎng)絡(luò)資源競爭悼瓮,間接導(dǎo)致服務(wù)端接收到的請求數(shù)達(dá)不到服務(wù)端的處理能力上限。
2艰猬、連接池
最大連接數(shù)太少横堡,造成請求等待。連接池一般分為服務(wù)器中間件連接池(比如Tomcat)和數(shù)據(jù)庫連接池(或者理解為最大允許連接數(shù)也行)冠桃。
3命贴、垃圾回收機(jī)制
從常見的應(yīng)用服務(wù)器來說,比如Tomcat食听,如果堆內(nèi)存設(shè)置比較小胸蛛,就會(huì)造成新生代的Eden區(qū)頻繁的進(jìn)行Young GC,老年代的Full GC也回收較頻繁樱报,那么對TPS也是有一定影響的葬项,因?yàn)槔厥諘r(shí)通常會(huì)暫停所有線程的工作。
4迹蛤、數(shù)據(jù)庫
高并發(fā)情況下民珍,如果請求數(shù)據(jù)需要寫入數(shù)據(jù)庫,且需要寫入多個(gè)表的時(shí)候盗飒,如果數(shù)據(jù)庫的最大連接數(shù)不夠嚷量,或者寫入數(shù)據(jù)的SQL沒有索引沒有綁定變量,抑或沒有主從分離逆趣、讀寫分離等蝶溶,就會(huì)導(dǎo)致數(shù)據(jù)庫事務(wù)處理過慢,影響到TPS宣渗。
5抖所、硬件資源
包括CPU(配置、使用率等)落包、內(nèi)存(占用率等)部蛇、磁盤(I/O、頁交換等)咐蝇。
6涯鲁、壓力機(jī)
比如Jmeter和Loadrunner,單機(jī)負(fù)載能力有限有序,如果需要模擬的用戶請求數(shù)超過其負(fù)載極限抹腿,也會(huì)間接影響TPS(這個(gè)時(shí)候就需要進(jìn)行分布式壓測來解決其單機(jī)負(fù)載的問題)。
7旭寿、業(yè)務(wù)邏輯
業(yè)務(wù)解耦度較低警绩,較為復(fù)雜,整個(gè)事務(wù)處理線被拉長也會(huì)導(dǎo)致TPS上不去盅称。
8肩祥、系統(tǒng)架構(gòu)
比如是否有緩存服務(wù)后室,緩存服務(wù)器配置,緩存命中率混狠、緩存穿透以及緩存過期等岸霹,都會(huì)影響到測試結(jié)果。
八将饺、500或503錯(cuò)誤
現(xiàn)象:壓測過程中贡避,服務(wù)器返回500或503錯(cuò)誤
排查手段
(1)通過瀏覽器訪問網(wǎng)站予弧,如果瀏覽器打不開刮吧,服務(wù)器可能掛了∫锤颍可能的原因是:內(nèi)存溢出杀捻、數(shù)據(jù)庫連接池滿了、線程死鎖坠七。
∷住(2)先看JVM內(nèi)存是否滿了:jstat -gcutil 2384 1000 5
(3)看數(shù)據(jù)庫連接池大小是否占滿彪置,把最大連接數(shù)改大拄踪,如果還是出現(xiàn)這種現(xiàn)象,證明是數(shù)據(jù)庫連接池不釋放拳魁。
』掏(4)最后看是否有線程死鎖:jstack -l 進(jìn)程號(hào)
九、性能問題分析流程
1潘懊、查看服務(wù)器的CPU姚糊、內(nèi)存 、負(fù)載等情況授舟,包括應(yīng)用服務(wù)器和數(shù)據(jù)庫服務(wù)器
2救恨、查看數(shù)據(jù)庫健康狀態(tài),數(shù)據(jù)庫死鎖释树、連接池不釋放
3肠槽、查看項(xiàng)目日志(查看無報(bào)錯(cuò)現(xiàn)象)
4、查看jvm的gc等情況