Java服務(wù)器宕機(jī)的解決方法論
1 宕機(jī)概要
1.1 定義
向服務(wù)器的請求都沒有響應(yīng)或者響應(yīng)非常慢。
前端界面的崩潰并非宕機(jī)。
1.2 分類
進(jìn)程閃退
- 內(nèi)部崩潰
- 外部終止
線程鎖死或者無限等待
內(nèi)存溢出
下面分別進(jìn)行詳解
2 進(jìn)程閃退
2.1 內(nèi)部崩潰
JVM 發(fā)生內(nèi)部崩潰,必然會生成"hs_err_pid"開頭的文件。
下面講一種常見情況:
無法申請內(nèi)存,顯示commit_memory錯(cuò)誤
Current thread (0x00007f3e40013000): JavaThread "Unknown thread" [_thread_in_vm, id=11408, stack(0x00007f3e49983000,0x00007f3e49a84000)] Stack: [0x00007f3e49983000,0x00007f3e49a84000], sp=0x00007f3e49a82360, free space=1020k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V [libjvm.so+0x9a32da] VMError::report_and_die()+0x2ea V [libjvm.so+0x497f7b] report_vm_out_of_memory(char const*, int, unsigned long, char const*)+0x9b V [libjvm.so+0x81fcce] os::Linux::commit_memory_impl(char*, unsigned long, bool)+0xfe V [libjvm.so+0x820219] os::pd_commit_memory(char*, unsigned long, unsigned long, bool)+0x29 V [libjvm.so+0x819faa] os::commit_memory(char*, unsigned long, unsigned long, bool)+0x2a V [libjvm.so+0x99eae9] VirtualSpace::expand_by(unsigned long, bool)+0x1c9 V [libjvm.so+0x99ec6d] VirtualSpace::initialize(ReservedSpace, unsigned long)+0xcd V [libjvm.so+0x57962f] CardGeneration::CardGeneration(ReservedSpace, unsigned long, int, GenRemSet*)+0x11f V [libjvm.so+0x46ceed] ConcurrentMarkSweepGeneration::ConcurrentMarkSweepGeneration(ReservedSpace, unsigned long, int, CardTableRS*, bool, FreeBlockDictionary<FreeChunk>::DictionaryChoice)+0x5d V [libjvm.so+0x57a906] GenerationSpec::init(ReservedSpace, int, GenRemSet*)+0x106 V [libjvm.so+0x56afe4] GenCollectedHeap::initialize()+0x344 V [libjvm.so+0x9751aa] Universe::initialize_heap()+0xca V [libjvm.so+0x976379] universe_init()+0x79 V [libjvm.so+0x5b1d25] init_globals()+0x65 V [libjvm.so+0x95dc6d] Threads::create_vm(JavaVMInitArgs*, bool*)+0x1ed V [libjvm.so+0x639fe4] JNI_CreateJavaVM+0x74
這一般是因?yàn)?Xmx 設(shè)置過大,超過系統(tǒng)可用內(nèi)存,JVM 申請內(nèi)存失敗。
比如服務(wù)器總內(nèi)存32G ,同時(shí)運(yùn)行多個(gè)程序,程序 A 配了20GXmx,其他程序也配了20G Xmx ,Linux的交換空間也沒有設(shè)置,這時(shí)候如果其他程序用滿20G內(nèi)存那么服務(wù)的可用內(nèi)存必然低于12G,這時(shí)如果Tomcat需要大于12G的內(nèi)存就很容易發(fā)生該錯(cuò)誤,直接宕機(jī)!
解決方案
- 減少Xmx值使得所有的綜合不超過服務(wù)器物理內(nèi)存
- 調(diào)整 Xms=Xmx
- 服務(wù)器不要運(yùn)行其他不必要的東西
- 配置一部分swap空間(虛擬內(nèi)存)
2.2 外部終止
如果找不到"hs_err_pid"開頭的文件,那么這個(gè)進(jìn)程的閃退必然是被從外部終止的。
2.2.1 OOMKiller
java長期內(nèi)存占用過高,系統(tǒng)需要內(nèi)存使用的時(shí)候沒有內(nèi)存,Linux的oomkiller機(jī)制會干掉最低優(yōu)先級的內(nèi)存
檢查 /var/logs/message , /var/logs/dmesg或者對應(yīng)日期文件,看看有沒有類似下面的內(nèi)容,日志有時(shí)間可以判斷
2.2.2 SSH注銷
檢查/var/log/auth.log,/var/log/secure或者對應(yīng)日期的文件,檢查宕機(jī)的時(shí)間點(diǎn)有沒有
時(shí)間吻合,那么宕機(jī)原因即可確認(rèn)。
解決方案
使用nohup命令在后臺運(yùn)行啟動(dòng)程序,檢查ssh注銷原因
2.2.3 其他人為因素
不是很好判斷,需要給shell加上操作記錄
3 線程鎖死/無限等待
表現(xiàn)
系統(tǒng)無法訪問時(shí),當(dāng)前cpu占用非常低
使用 jstack命令輸出線程堆棧即可
jstack pid >> 1.txt or jstack -F pid >> 1.txt
都行,或者用jprofiler工具看堆棧,或者其他任何可以拿到堆棧的工具都可以, java的堆棧就是java方法調(diào)用的路徑,可以定位一些簡單的問題
4 內(nèi)存溢出
現(xiàn)象
CPU全部占滿,內(nèi)存達(dá)到配置Xmx最大值
4.1 CPU占滿緣由
并不是 CPU 不夠用,而是涉及到JVM的GC 機(jī)制,大部分情況來說CPU都是過剩的
JVM 使用GC的方法來回收沒有被引用的內(nèi)存塊,在當(dāng)前的回收機(jī)制中,回收器是并發(fā)進(jìn)行的,回收的線程個(gè)數(shù)有一個(gè)公式:
當(dāng)CPU核心數(shù)
小于8
- 1個(gè)核心對應(yīng)一個(gè)gc線程
大于8
- gc的線程數(shù)= 8 + ((N - 8) * 5/8)
N代表核心的數(shù)量,這是默認(rèn)的gc線程創(chuàng)建公式
threads = N <= 8 ? N : (8 + ((N - 8) * 5/8))
當(dāng)然也可以通過參數(shù) -XX:ParallelCMSThreads=20 來配置 GC 線程數(shù),就不會使用默認(rèn)的設(shè)置,默認(rèn)情況下不要調(diào)整,因?yàn)檎{(diào)了也沒什么卵用,最多在宕機(jī)的時(shí)候cpu占用按照你設(shè)定的值來。
當(dāng)發(fā)生內(nèi)存溢出的時(shí)候,或者快要內(nèi)存溢出的時(shí)候,不一定是內(nèi)存溢出,JVM 發(fā)現(xiàn)內(nèi)存不夠了,就會 GC,所有線程開始工作,暫停 JVM 運(yùn)行,開始回收,如果回收到內(nèi)存了,ok,jvm可以正確繼續(xù)執(zhí)行,
這也就是為什么有時(shí)候配置內(nèi)存溢出的參數(shù)沒有自動(dòng)生成dump的原因,因?yàn)樗苓\(yùn)行,但是比較慢,所以沒有OOM,就不會生成dump,
如果沒有回收到什么內(nèi)存,gc會循環(huán)持續(xù)執(zhí)行,這就導(dǎo)致了cpu全部占滿的現(xiàn)象,所以說內(nèi)存溢出的時(shí)候,一定伴隨cpu占滿(按照設(shè)置或者公式計(jì)算的線程量)
4.2 JVM內(nèi)存分配機(jī)制
在說說JVM怎么分配內(nèi)存的,大家都知道給客戶配置Xmx參數(shù)和xms參數(shù),Xmx代表的是最大堆內(nèi)存,xms代表的是最小堆內(nèi)存,至于permsize就和這些都沒有關(guān)系,不能算在內(nèi)存溢出,遇到拋錯(cuò)outofmemory permsize什么的調(diào)大就行了
permsize是一個(gè)被jvm也拋棄的參數(shù)只存在1.7之前的jdk中,是用來保存java的class等內(nèi)容的存儲空間,1.8被metaspace替代
這個(gè)內(nèi)存怎么不回收的啊,一問都是在任務(wù)管理器看的!這個(gè)地方是看不到內(nèi)存回收的,或者說他也會回收,但是可能要等個(gè)好幾天才會回收一次,可以忽略這種機(jī)制的存在
形而上學(xué)
WC 論
如果把內(nèi)存比喻成茅坑,操作系統(tǒng)64g內(nèi)存就是一共64個(gè)茅坑,那么JVM的內(nèi)存回收相當(dāng)于茅坑調(diào)度系統(tǒng),每個(gè)gc線程相當(dāng)于調(diào)度系統(tǒng)派出去的茅坑檢查員,給jvm設(shè)置了 Xms=2g, Xmx=32g,那么程序啟動(dòng),jvm直接占了兩個(gè)茅坑,任務(wù)管理器看到內(nèi)存占用2g,即使沒人上廁所,JVM也不會把坑還給操作系統(tǒng)。
假設(shè)一個(gè)人上廁所10秒,一開始的時(shí)候 20秒有一個(gè)人來上廁所,那么 jvm通過茅坑檢查員發(fā)現(xiàn)哎兩個(gè)坑總有一個(gè)是空的,維持茅坑數(shù)量不變,內(nèi)存的占用一直是2g,過了些時(shí)候,來的人開始增多了,變成5秒有一個(gè)人來上廁所,茅坑檢查員向JVM匯報(bào)有人開始有排隊(duì)了,兩個(gè)坑位很緊張,不行要多弄幾個(gè)坑才行,于是,jvm向系統(tǒng)又申請了兩個(gè)坑,任務(wù)管理器可以看到內(nèi)存占用變成了4個(gè)G,這時(shí)候又突然發(fā)生壓力增大,變成了1秒來一個(gè)人,4個(gè)坑肯定不夠啊,于是jvm又把內(nèi)存擴(kuò)容到10-11g,現(xiàn)在夠用了,任務(wù)管理器會看到內(nèi)存一直維持在10-11g,終于大家都上完廁所了,沒人排隊(duì)了,茅坑都空出來了。
但是,jvm是個(gè)霸道總裁,被他占的東西,除非死不然不會吐出來的,所以任務(wù)管理器里面看到內(nèi)存還是10-11g不會降低,除非jvm死了,實(shí)際沒有任何內(nèi)存占用(所以不要再說內(nèi)存不回收的問題,這個(gè)內(nèi)存的回收不回收和宕機(jī)是沒有直接關(guān)系的)
如果這時(shí)候突然一下子來了很多很多人,比如一下子來了64個(gè)人要上廁所,這時(shí)候會怎樣了,JVM把他的所有的茅坑檢查員都派出去檢查啊,然后發(fā)現(xiàn)完蛋了茅坑不夠用啊,申請到32個(gè)都不夠用啊,于是jvm的特派茅坑檢查員就一個(gè)坑一個(gè)坑的拍,一個(gè)坑一個(gè)坑的催,結(jié)果呢,檢查員在催,大家就拉不出來了,上廁所的時(shí)間無限期延長,外面的人要進(jìn)去,里面的人出不來,BOOM,廁所就不響應(yīng)了,后面來的人都拉褲子了。
怎么解決?
- 換個(gè)茅坑管理員,更好的調(diào)度茅坑檢查員和分配茅坑,這就有了G1回收器 ,茅坑越多效果越好,目前JDK情況內(nèi)存大于10G的情況G1的效果好于CMS,低于10G的情況下不如CMS
- 從源頭控制人員,不要一下子來這么多人(申請內(nèi)存),也就是常見的不要讓業(yè)務(wù)查大量數(shù)據(jù)占內(nèi)存。
而上面講的線程鎖死的情況要做類比的話,就是32個(gè)坑唄32個(gè)人占了,還死活不肯出來,導(dǎo)致后面排隊(duì)的人失去響應(yīng)了。
沒有味道的比喻
解釋一下java的面向?qū)ο蠛蛯ο笠?
一棟大樓,10層共1000個(gè)工位 (類比物理內(nèi)存)。
包給一個(gè)二房東 中介公司Z (jvm)。
中介公司和大樓物業(yè)談好彈性繳費(fèi),租多少出去收多少錢。
Z公司先一下租300個(gè)位置 (類比Xms)省錢,
Z公司和物業(yè)談好最多租600個(gè)位置(類比Xmx)。
Z公司找到了公司A(200人)來這里 就占用了200個(gè)工位 (類比一次數(shù)據(jù)查詢)。
公司A是一個(gè)大的對象,每個(gè)人類比最小的單元格,每個(gè)小團(tuán)隊(duì)也是一個(gè)對象,個(gè)人被小團(tuán)隊(duì)引用,小團(tuán)隊(duì)又被更上級的比如產(chǎn)品,比如大技術(shù)支持大團(tuán)隊(duì)引用,大團(tuán)隊(duì)又被公司引用,最終公司這個(gè)大對象占用了200工位,類比下來200個(gè)工位內(nèi)存不釋放的根就是這個(gè)公司在這兒上班。
這時(shí)候公司A倒閉了,200個(gè)工位就空出來了(內(nèi)存釋放)。
- 內(nèi)存溢出宕機(jī)是什么情況呢?
- 找Z公司租工位的公司,總工位超過了600,總不能坐大腿上上班啊,于是物業(yè)不會給Z工位的,合同寫的好好的,Z公司不滿足客戶需求,運(yùn)作不起來破產(chǎn)倒閉。
- 經(jīng)常遇到的申請內(nèi)存失敗的崩潰是什么情況?
- 物業(yè)是個(gè)滑頭,不止找了Z公司一家中介,還有Y公司也是做中介的(類比兩個(gè)JVM)。都承諾Z和Y公司都是最多可以租600個(gè)位置。初始都租的300個(gè)位置,大家相處融洽,隨著公司不停入住,矛盾出現(xiàn)了:
- Y公司效益比較好,先找了公司,已經(jīng)占了600位置;
- 這時(shí)候Z公司的效益也上來了,也要增加工位 (類比申請內(nèi)存),這時(shí)候物業(yè)根本沒有位置能給他。于是Z公司運(yùn)轉(zhuǎn)不下去,破產(chǎn)倒閉
5 總結(jié)
宕機(jī)分析的目的就是要找到占用內(nèi)存的東西,把他找出來,找出他的原因,然后把它改掉。JVM的內(nèi)存對象分配相當(dāng)于一顆樹,所有的對象都被層層引用,直到GCRoot根節(jié)點(diǎn),如果沒有根節(jié)點(diǎn)的引用,這個(gè)對象是完全可以直接釋放掉的,大部分也是因?yàn)間cRoot存在的對象過多導(dǎo)致的宕機(jī),當(dāng)然也不排除可以使用已經(jīng)回收的對象來分析,由于生成dump的時(shí)間不精確,可能他生成的時(shí)候 ,對應(yīng)的大組件已經(jīng)回收了,但是jvm緩過來還需要一些時(shí)間,所以還是處于大量gc的狀態(tài),這時(shí)候只能通過對于引用的檢索找到最多的引用對象來進(jìn)行分析。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)截取PDF指定頁并進(jìn)行圖片格式轉(zhuǎn)換功能
這篇文章主要介紹了java實(shí)現(xiàn)截取PDF指定頁并進(jìn)行圖片格式轉(zhuǎn)換功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09通過Java?Reflection實(shí)現(xiàn)編譯時(shí)注解正確處理方法
Java注解是一種標(biāo)記在JDK5及以后的版本中引入,用于Java語言中向程序添加元數(shù)據(jù)的方法,這篇文章主要介紹了通過Java?Reflection實(shí)現(xiàn)編譯時(shí)注解處理方法,需要的朋友可以參考下2023-06-06PowerJob的ServerDiscoveryService工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的ServerDiscoveryService工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Elasticsearch算分優(yōu)化方案之rescore_query示例詳解
這篇文章主要為大家介紹了Elasticsearch算分優(yōu)化方案之rescore_query示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08使用Java對數(shù)據(jù)庫進(jìn)行基本的查詢和更新操作
這篇文章主要介紹了使用Java對數(shù)據(jù)庫進(jìn)行基本的查詢和更新操作,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-10-10Spring Data JPA+kkpager實(shí)現(xiàn)分頁功能實(shí)例
本篇文章主要介紹了Spring Data JPA+kkpager實(shí)現(xiàn)分頁功能實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06SpringBoot整合redis+Aop防止重復(fù)提交的實(shí)現(xiàn)
Spring Boot通過AOP可以實(shí)現(xiàn)防止表單重復(fù)提交,本文主要介紹了SpringBoot整合redis+Aop防止重復(fù)提交的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07SpringMVC 通過commons-fileupload實(shí)現(xiàn)文件上傳功能
這篇文章主要介紹了SpringMVC 通過commons-fileupload實(shí)現(xiàn)文件上傳,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02