現(xiàn)象
今天峦失, 生產(chǎn)上的springboot 應用cpu 達到200%扇丛, 即占用了2核, 線上應用奔潰尉辑, 應用無法訪問帆精。在立刻重啟應用后,應用恢復正常隧魄, 奇怪的是卓练, 在一段時間后, 服務又出現(xiàn)無法訪問的情況购啄。
問題分析
該問題可以大致上看成2類襟企, 可能也可能沒有直接關聯(lián)
- 服務宕機
- 應用占用CPU很高
排查思路及手段
因為不同的問題排查思路及方式會不同, 針對本次服務宕機的排查問題追溯如下:
通過jps查看應用是否運行 > 通過Top查看系統(tǒng)運行狀況 > 查看錯誤日志 >
排查細節(jié):
1狮含、 使用jps查看是哪個進程編號pid 對應哪個應用
jps: Java Virtual Machine Process Status Tool
[root@localhost ~]# jps
26610 Bootstrap
28877 jar
18039 TSDMain
26091 Kafka
19456 Jps
如果發(fā)現(xiàn)應用不在列表中顽悼, 則本次服務無法訪問則是因為應用未啟動造成, 則需要排查是什么原因造成服務未啟動几迄。
可能原因:
- linux oom killer 將應用殺死
- 人為殺死(可能性很形盗)
- 操作系統(tǒng)宕機等原因(如果操作系統(tǒng)在云服務上,可能性極杏承病)
此時坤溃, 我發(fā)現(xiàn)應用還在列表中屯断。
2. 查看錯誤日志
java 應用在線上會以文件的形式記錄warn級別及以上的錯誤日志诽俯, 發(fā)現(xiàn)有大量以下內容的錯誤日志:
"Cannot serialize; nested exception is
org.springframework.core.serializer.support.SerializationFailedException: Failed to
serialize object using DefaultSerializer; nested exception is java.lang.OutOfMemoryError:
PermGen space"
以上錯誤很明顯了: 內存溢出, 永久代內存空間不足务荆。
2.1查看應用永久代內存空間大小
> jstat -gcpermcapacity pid #perm對象的信息及其占用量
PGCMN PGCMX PGC PC YGC FGC FGCT GCT
21504.0 83968.0 83968.0 83968.0 361 2 0.906 39.789
# 同時我們可以通過jstat -gcutil來動態(tài)查看gc的狀態(tài)
> jstat -gcutil pid
大致情況如下:
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 11.13 11.97 40.61 33.67 5508 44.783 22 10.686 55.469
(以上是jvm參數(shù)加入后的狀態(tài), 之前看到P是99%以上)
經(jīng)過換算穷遂, 發(fā)現(xiàn)perm代的最大容量才81M函匕。
本次解決辦法:
設置jvm參數(shù), 增大永久代內存的大小
(java -XX:MaxPermSize=512M -jar xxxx.jar &)
之后服務不再出現(xiàn)無法提供服務的問題蚪黑。
事后回顧及思考:
應用通過jenkins發(fā)布盅惜, 構建后執(zhí)行腳本運行springboot的應用, 采用的是java -jar xxx.jar 忌穿, 故采用默認配置抒寂, 從而忽略了生產(chǎn)對于內存要求的苛刻性。
所以對于生產(chǎn)環(huán)境的配置掠剑, 往往要結合并分析實際業(yè)務場景屈芜, 選擇適合的配置, 當然有時候不能盲目加入硬件配置朴译, 也可以通過一定的邏輯優(yōu)化來解決問題井佑。
知識補充:(針對該現(xiàn)象)
- 內存中什么對象是永久代
有時候把永生代和方法區(qū)對等, 方法區(qū)是多個線程共享的區(qū)域眠寿, 存儲常量信息躬翁,類信息,方法信息盯拱。
- JVM性能調優(yōu)監(jiān)控工具 使用
往往大多數(shù)程序員忽視了這方面的知識盒发, 對關于jvm的問題束手無策, 其實jdk中為我們提供了大量的jvm調優(yōu)及監(jiān)控工具狡逢, 比如jps宁舰、jstack、jmap奢浑、jhat明吩、jstat。
在本次我們使用了jps和jstat來查看問題殷费, 當然這些命令還有其他參數(shù),如果感興趣的話可以在網(wǎng)上搜索低葫。
針對CPU占用高的問題
系統(tǒng)CPU占用高曾經(jīng)也出現(xiàn)過详羡,所以很快能夠排查出大致問題所在。
大致思路
1. 檢查是哪個進程占用了高cpu
> top # top命令經(jīng)常用來監(jiān)控linux的系統(tǒng)狀況
有時候會看到mysql進程占用很高cpu
一般mysql的配置dba會配置好嘿悬,往往不是mysql配置的問題实柠, 所以 對于開發(fā)人員來說, 首先看慢sql日志善涨。如果此時發(fā)現(xiàn)有大量慢sql日志產(chǎn)生窒盐, 則需要結合實際業(yè)務場景進行相應的sql及邏輯優(yōu)化草则, 有必要的話可以通過一些緩存手段來解決問題。
如果是java 應用占用很高cpu
2 java應用CPU占用高
這時候我們開始對具體的應用進行分析了蟹漓, 我們可以借用一些工具來分析java進程炕横。如
jstack、jmap葡粒、jhat份殿、hprof。
我的做法是首先定位占用高cpu的線程:
步驟如下:
- 通過jps獲取進程id
- 通過jstack 查找線程dump
> ps -mp pid -o THREAD,tid,time
將線程id轉換為16進制(以方便找到dump中對應的線程)
> printf "%x\n" tid
> jstack pid |grep tid -A 60 這里的tid為轉換的16進制嗽交, grep -A n 為匹配的行數(shù)后n條
查詢結果大致如下:
"container-0" prio=10 tid=0x00007f9a14e1e800 nid=0x465a waiting on condition [0x00007f9a6dc57000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:427)
at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer$1.run(TomcatEmbeddedServletContainer.java:177)
"VM Thread" prio=10 tid=0x00007f9a7812f800 nid=0x45ec runnable
"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007f9a7801e000 nid=0x45dd runnable
如果定位發(fā)現(xiàn)cpu高的線程為GC卿嘲, 很有可能是GC出現(xiàn)了問題。
如果線程dump中執(zhí)行的是某業(yè)務代碼夫壁, 可能是發(fā)生了死循環(huán)之類的拾枣。
參考:
[1] jstat 命令詳解
[2] Java SE Specifications Chapter 2. The Structure of the Java Virtual Machine
[3] 《深入理解java虛擬機 JVM高級特性與最佳實踐》