日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java 應(yīng)用線上問題排查思路、常用工具小結(jié)

 鷹兔牛熊眼 2020-05-20

作者:Richard_Y

ricstudio.top/archives/java-online-question-probe

前言

本文總結(jié)了一些Java應(yīng)用線上常見問題的定位步驟,分享的主要目的是想讓對線上問題接觸少的同學(xué)有個(gè)預(yù)先認(rèn)知,免得在遇到實(shí)際問題時(shí)手忙腳亂。畢竟作者自己也是從手忙腳亂時(shí)走過來的。

只不過這里先提示一下。在線上應(yīng)急過程中要記住,只有一個(gè)總體目標(biāo):「盡快恢復(fù)服務(wù),消除影響」。不管處于應(yīng)急的哪個(gè)階段,我們首先必須想到的是恢復(fù)問題,恢復(fù)問題不一定能夠定位問題,也不一定有完美的解決方案,也許是通過經(jīng)驗(yàn)判斷,也許是預(yù)設(shè)開關(guān)等,但都可能讓我們達(dá)到快速恢復(fù)的目的,然后保留部分現(xiàn)場,再去定位問題、解決問題和復(fù)盤。

好,現(xiàn)在讓我們進(jìn)入正題吧。

一、CPU 利用率高/飆升

?

注:CPU使用率是衡量系統(tǒng)繁忙程度的重要指標(biāo)。但是「CPU使用率的安全閾值是相對的,取決于你的系統(tǒng)的IO密集型還是計(jì)算密集型」。一般計(jì)算密集型應(yīng)用CPU使用率偏高load偏低,IO密集型相反。

?

「常見原因:」

  • 頻繁 gc
  • 死循環(huán)、線程阻塞、io wait...etc

模擬

這里為了演示,用一個(gè)最簡單的死循環(huán)來模擬CPU飆升的場景,下面是模擬代碼,

在一個(gè)最簡單的SpringBoot Web 項(xiàng)目中增加CpuReaper這個(gè)類,

/**
* 模擬 cpu 飆升場景
* @author Richard_yyf
*/
@Component
public class CpuReaper {

@PostConstruct
public void cpuReaper() {
int num = 0;
long start = System.currentTimeMillis() / 1000;
while (true) {
num = num + 1;
if (num == Integer.MAX_VALUE) {
System.out.println('reset');
num = 0;
}
if ((System.currentTimeMillis() / 1000) - start > 1000) {
return;
}
}
}
}

打包成jar之后,在服務(wù)器上運(yùn)行。java -jar cpu-reaper.jar &

第一步:定位出問題的線程

方法 a: 傳統(tǒng)的方法

  1. top 定位CPU 最高的進(jìn)程

    執(zhí)行top命令,查看所有進(jìn)程占系統(tǒng)CPU的排序,定位是哪個(gè)進(jìn)程搞的鬼。在本例中就是咱們的java進(jìn)程。PID那一列就是進(jìn)程號(hào)。(對指示符含義不清楚的見【附錄】)

  2. top -Hp pid  定位使用 CPU 最高的線程

  3. printf '0x%x' tid  線程 id 轉(zhuǎn)化 16 進(jìn)制

   > printf '0x%x' 12817
> 0x3211
  1. jstack pid | grep tid  找到線程堆棧
> jstack 12816 | grep 0x3211 -A 30

方法 b: show-busy-java-threads

https://raw.github.com/oldratlee/useful-scripts/release-2.x/bin/show-busy-java-threads

這個(gè)腳本來自于github上一個(gè)開源項(xiàng)目,項(xiàng)目提供了很多有用的腳本,show-busy-java-threads就是其中的一個(gè)。使用這個(gè)腳本,可以直接簡化方法A中的繁瑣步驟。如下,

> wget --no-check-certificate https://raw.github.com/oldratlee/useful-scripts/release-2.x/bin/show-busy-java-threads
> chmod +x show-busy-java-threads

> ./show-busy-java-threads
show-busy-java-threads
# 從所有運(yùn)行的Java進(jìn)程中找出最消耗CPU的線程(缺省5個(gè)),打印出其線程棧

# 缺省會(huì)自動(dòng)從所有的Java進(jìn)程中找出最消耗CPU的線程,這樣用更方便
# 當(dāng)然你可以手動(dòng)指定要分析的Java進(jìn)程Id,以保證只會(huì)顯示你關(guān)心的那個(gè)Java進(jìn)程的信息
show-busy-java-threads -p <指定的Java進(jìn)程Id>

show-busy-java-threads -c <要顯示的線程棧數(shù)>

方法 c: arthas thread

阿里開源的arthas現(xiàn)在已經(jīng)幾乎包攬了我們線上排查問題的工作,提供了一個(gè)很完整的工具集。在這個(gè)場景中,也只需要一個(gè)thread -n命令即可。擴(kuò)展:阿里問題定位神器 Arthas 操作實(shí)踐,定位線上BUG,超給力

https://github.com/alibaba/arthas

> curl -O https://arthas./arthas-boot.jar # 下載
?

要注意的是,arthas的cpu占比,和前面兩種cpu占比統(tǒng)計(jì)方式不同。前面兩種針對的是Java進(jìn)程啟動(dòng)開始到現(xiàn)在的cpu占比情況,arthas這種是一段采樣間隔內(nèi),當(dāng)前JVM里各個(gè)線程所占用的cpu時(shí)間占總cpu時(shí)間的百分比。

具體見官網(wǎng):https://alibaba./arthas/thread.html

?

后續(xù)

通過第一步,找出有問題的代碼之后,觀察到線程棧之后。我們「就要根據(jù)具體問題來具體分析」。這里舉幾個(gè)例子。

情況一:發(fā)現(xiàn)使用CPU最高的都是GC 線程。

GC task thread#0 (ParallelGC)' os_prio=0 tid=0x00007fd99001f800 nid=0x779 runnable
GC task thread#1 (ParallelGC)' os_prio=0 tid=0x00007fd990021800 nid=0x77a runnable
GC task thread#2 (ParallelGC)' os_prio=0 tid=0x00007fd990023000 nid=0x77b runnable
GC task thread#3 (ParallelGC)' os_prio=0 tid=0x00007fd990025000 nid=0x77c runnabl

gc 排查的內(nèi)容較多,所以我決定在后面單獨(dú)列一節(jié)講述。

情況二:發(fā)現(xiàn)使用CPU最高的是業(yè)務(wù)線程

  • io wait
    • 比如此例中,就是因?yàn)榇疟P空間不夠?qū)е碌膇o阻塞

https://blog.csdn.net/lonyness/article/details/82628988

  • 等待內(nèi)核態(tài)鎖,如 synchronized
    • jstack -l pid | grep BLOCKED  查看阻塞態(tài)線程堆棧
    • dump 線程棧,分析線程持鎖情況。
    • arthas提供了thread -b,可以找出當(dāng)前阻塞其他線程的線程。針對 synchronized 情況

二、頻繁 GC

1. 回顧GC流程

在了解下面內(nèi)容之前,請先花點(diǎn)時(shí)間回顧一下GC的整個(gè)流程。


接前面的內(nèi)容,這個(gè)情況下,我們自然而然想到去查看gc 的具體情況。

  • 方法a : 查看gc 日志
  • 方法b : jstat -gcutil 進(jìn)程號(hào) 統(tǒng)計(jì)間隔毫秒 統(tǒng)計(jì)次數(shù)(缺省代表一致統(tǒng)計(jì)
  • 方法c : 如果所在公司有對應(yīng)用進(jìn)行監(jiān)控的組件當(dāng)然更方便(比如Prometheus + Grafana)

這里對開啟 gc log 進(jìn)行補(bǔ)充說明。一個(gè)常常被討論的問題(慣性思維)是在生產(chǎn)環(huán)境中GC日志是否應(yīng)該開啟。因?yàn)樗a(chǎn)生的開銷通常都非常有限,因此我的答案是需要「開啟」。但并不一定在啟動(dòng)JVM時(shí)就必須指定GC日志參數(shù)。

?

HotSpot JVM有一類特別的參數(shù)叫做可管理的參數(shù)。對于這些參數(shù),可以在運(yùn)行時(shí)修改他們的值。我們這里所討論的所有參數(shù)以及以“PrintGC”開頭的參數(shù)都是可管理的參數(shù)。這樣在任何時(shí)候我們都可以開啟或是關(guān)閉GC日志。比如我們可以使用JDK自帶的jinfo工具來設(shè)置這些參數(shù),或者是通過JMX客戶端調(diào)用HotSpotDiagnostic MXBean的setVMOption方法來設(shè)置這些參數(shù)。

這里再次大贊arthas??,它提供的vmoption命令可以直接查看,更新VM診斷相關(guān)的參數(shù)。

?

獲取到gc日志之后,可以上傳到GC easy幫助分析,得到可視化的圖表分析結(jié)果。

http:///

2. GC 原因及定位

「prommotion failed」

從S區(qū)晉升的對象在老年代也放不下導(dǎo)致 FullGC(fgc 回收無效則拋 OOM)。

可能原因:

  • 「survivor 區(qū)太小,對象過早進(jìn)入老年代」

    查看 SurvivorRatio 參數(shù)

  • 「大對象分配,沒有足夠的內(nèi)存」

    dump 堆,profiler/MAT 分析對象占用情況

  • 「old 區(qū)存在大量對象」

    dump 堆,profiler/MAT 分析對象占用情況

你也可以從full GC 的效果來推斷問題,正常情況下,一次full GC應(yīng)該會(huì)回收大量內(nèi)存,所以 「正常的堆內(nèi)存曲線應(yīng)該是呈鋸齒形」。如果你發(fā)現(xiàn)full gc 之后堆內(nèi)存幾乎沒有下降,那么可以推斷:「堆中有大量不能回收的對象且在不停膨脹,使堆的使用占比超過full GC的觸發(fā)閾值,但又回收不掉,導(dǎo)致full GC一直執(zhí)行。「換句話來說,可能是」內(nèi)存泄露」了。

一般來說,GC相關(guān)的異常推斷都需要涉及到「內(nèi)存分析」,使用jmap之類的工具dump出內(nèi)存快照(或者 Arthas的heapdump)命令,然后使用MAT、JProfiler、JVisualVM等可視化內(nèi)存分析工具。

至于內(nèi)存分析之后的步驟,就需要小伙伴們根據(jù)具體問題具體分析啦。

三、線程池異常

Java 線程池以有界隊(duì)列的線程池為例,當(dāng)新任務(wù)提交時(shí),如果運(yùn)行的線程少于 corePoolSize,則創(chuàng)建新線程來處理請求。如果正在運(yùn)行的線程數(shù)等于 corePoolSize 時(shí),則新任務(wù)被添加到隊(duì)列中,直到隊(duì)列滿。當(dāng)隊(duì)列滿了后,會(huì)繼續(xù)開辟新線程來處理任務(wù),但不超過 maximumPoolSize。當(dāng)任務(wù)隊(duì)列滿了并且已開辟了最大線程數(shù),此時(shí)又來了新任務(wù),ThreadPoolExecutor 會(huì)拒絕服務(wù)。

常見問題和原因

這種線程池異常,一般有以下幾種原因:

  1. 「下游服務(wù) 響應(yīng)時(shí)間(RT)過長」

    這種情況有可能是因?yàn)橄掠畏?wù)異常導(dǎo)致的,作為消費(fèi)者我們要設(shè)置合適的超時(shí)時(shí)間和熔斷降級機(jī)制。

    另外針對這種情況,一般都要有對應(yīng)的監(jiān)控機(jī)制:比如日志監(jiān)控、metrics監(jiān)控告警等,不要等到目標(biāo)用戶感覺到異常,從外部反映進(jìn)來問題才去看日志查。

  2. 「數(shù)據(jù)庫慢 sql 或者數(shù)據(jù)庫死鎖」

查看日志關(guān)鍵詞

  1. 「Java 代碼死鎖」

    jstack –l pid | grep -i –E 'BLOCKED | deadlock'

上述前兩種問題的排查辦法,一般都是通過查看日志或者一些監(jiān)控組件。

四、常見問題恢復(fù)

?

這一部分內(nèi)容參考自此篇文章

?

https://developer.aliyun.com/article/757655

五、Arthas

這里還是想單獨(dú)用一節(jié)安利一下Arthas這個(gè)工具。

Arthas 是阿里巴巴開源的Java 診斷工具,基于 Java Agent 方式,使用 Instrumentation 方式修改字節(jié)碼方式進(jìn)行 Java 應(yīng)用診斷。

  • dashboard :系統(tǒng)實(shí)時(shí)數(shù)據(jù)面板, 可查看線程,內(nèi)存,gc 等信息

  • thread :查看當(dāng)前線程信息,查看線程的堆棧,如查看最繁忙的前 n 線程

  • getstatic:獲取靜態(tài)屬性值,如 getstatic className attrName 可用于查看線上開關(guān)真實(shí)值

  • sc:查看 jvm 已加載類信息,可用于排查 jar 包沖突

  • sm:查看 jvm 已加載類的方法信息

  • jad:反編譯 jvm 加載類信息,排查代碼邏輯沒執(zhí)行原因

  • logger:查看logger信息,更新logger level

  • watch:觀測方法執(zhí)行數(shù)據(jù),包含出參、入?yún)?、異常?/p>

  • trace:方法內(nèi)部調(diào)用時(shí)長,并輸出每個(gè)節(jié)點(diǎn)的耗時(shí),用于性能分析

  • tt:用于記錄方法,并做回放

?

以上內(nèi)容節(jié)選自Arthas官方文檔。

?

https://alibaba./arthas/commands.html

另外,Arthas里的 還集成了 ognl 這個(gè)輕量級的表達(dá)式引擎,通過ognl,你可以用arthas 實(shí)現(xiàn)很多的“騷”操作。

其他的這里就不多說了,感興趣的可以去看看arthas的官方文檔、github issue。

六、涉及工具

再說下一些工具。

  • Arthas(超級推薦????)
  • useful-scripts
  • GC easy
  • Smart Java thread dump analyzer - thread dump analysis in seconds
  • PerfMa - Java虛擬機(jī)參數(shù)/線程dump/內(nèi)存dump分析
  • Linux 命令
  • Java  N 板斧
  • MAT、JProfiler...等可視化內(nèi)存分析工具

結(jié)語

我知道我這篇文章對于線上異常的歸納并不全面,還有「網(wǎng)絡(luò)(超時(shí)、TCP隊(duì)列溢出...)」、堆外內(nèi)存等很多的異常場景沒有涉及。主要是因?yàn)樽约航佑|很少,沒有深刻體會(huì)研究過,強(qiáng)行寫出來免不得會(huì)差點(diǎn)意思,更怕的是誤了別人??。

還有想說的就是,Java 應(yīng)用線上排查實(shí)際非??季恳粋€(gè)人基礎(chǔ)是否扎實(shí)、解決問題能力是否過關(guān)。比如線程池運(yùn)行機(jī)制、gc分析、Java 內(nèi)存分析等等,如果基礎(chǔ)不扎實(shí),看了更多的是一頭霧水。另外就是,多看看網(wǎng)上一些好的關(guān)于異常排查的經(jīng)驗(yàn)文章,這樣即使自己暫時(shí)遇不到,但是會(huì)在腦海里面慢慢總結(jié)出一套解決類似問題的結(jié)構(gòu)框架,到時(shí)候真的遇到了,也就是觸類旁通的事情罷了。

?

如果本文有幫助到你,希望能點(diǎn)個(gè)贊,這是對我的最大動(dòng)力????????。

?

參考

  • https://developer.aliyun.com/article/757655
  • Arthas 3.2.0 文檔
  • 《分布式服務(wù)架構(gòu):原理、設(shè)計(jì)與實(shí)戰(zhàn)》

附錄

top 命令顯示的指示符的含義

指示符含義
PID進(jìn)程id
USER進(jìn)程所有者
PR進(jìn)程優(yōu)先級
NInice值。負(fù)值表示高優(yōu)先級,正值表示低優(yōu)先級
VIRT進(jìn)程使用的虛擬內(nèi)存總量,單位kb。VIRT=SWAP+RES
RES進(jìn)程使用的、未被換出的物理內(nèi)存大小,單位kb。RES=CODE+DATA
SHR共享內(nèi)存大小,單位kb
S進(jìn)程狀態(tài)。D=不可中斷的睡眠狀態(tài) R=運(yùn)行 S=睡眠 T=跟蹤/停止 Z=僵尸進(jìn)程
%CPU上次更新到現(xiàn)在的CPU時(shí)間占用百分比
%MEM進(jìn)程使用的物理內(nèi)存百分比
TIME+進(jìn)程使用的CPU時(shí)間總計(jì),單位1/100秒
COMMAND進(jìn)程名稱(命令名/命令行)

Java面試題專欄

【71期】面試官:對并發(fā)熟悉嗎?談?wù)勀銓ava中常用的幾種線程池的理解

【72期】面試官:對并發(fā)熟悉嗎?說一下synchronized與Lock的區(qū)別與使用

【73期】面試官:Spring 和 Spring Boot 的區(qū)別是什么?

【74期】面試官:對多線程熟悉嗎,來談?wù)劸€程池的好處?

【75期】面試官:說說Redis的過期鍵刪除策略吧!(高頻)

【76期】面試官問:List如何一邊遍歷,一邊刪除?

【77期】這一道面試題就考驗(yàn)了你對Java的理解程度

【78期】別找了,Java集合面試問題這里幫你總結(jié)好了!

【79期】別找了,回答Spring中Bean的生命周期,這里幫你總結(jié)好了!

【80期】說出Java創(chuàng)建線程的三種方式及對比

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多