阿裡妹導讀:Java能成為應用最廣泛的語言,和他的記憶體托管機制是分不開的。很多人眼中,Java虛拟機是透明的,隻需知道核心api的用法,便可以專注于實作具體業務,然後依賴Java虛拟機運作甚至優化應用。
你是否有過這樣的經曆,跑得好好的Java程序,突然就癱瘓了。過于依賴Java虛拟機導緻我們對問題無從下手,問題反複出現影響開發效率。其實,多數Java程序癱瘓的原因可以從java虛拟機層面找到原因,本文列舉出導緻Java程序癱瘓的一些共性原因,供大家交流和學習。
一、記憶體回收一直是java的痛點
用Java無法做出類似Redis這樣的産品。java的記憶體回收機制使我們在編寫代碼時不需要關注對象的回收,同時加大了記憶體回收的消耗,标記複制需要做記憶體拷貝,标記清除算法則需要stop the world。是以我們在使用緩存的時候,量稍微大一些就需要借助類似Redis這樣的中間件幫我們處理了。作為Javaer,我們享受了自動記憶體回收的安逸,同時也需要多了解下記憶體優化的方法。
二、為什麼fgc停不下來了
1.什麼情況下會gc
為了了解我們的系統為什麼會不停fgc,我們需要先了解一下系統什麼情況下會gc。在jvm層面,當我們new一個對象的時候,jvm會先在堆區配置設定對象需要的記憶體,這個時候如果記憶體不夠的話,就需要gc了,gc的傳回結果就是對象的空間位址。jvm會先進行ygc,也就是我們通常說的标記複制,如果ygc之後依然申請不到空間,就會進行fgc了。同理,如果fgc之後依然沒有足夠的空間,就會循環的進行fgc,直到申請到足夠的空間。
2.導緻不停的fgc的原因
如上文所講,fgc有可能發生在你的每一行代碼。如果fgc之後依然沒有足夠的空間,就會不停的fgc,直到申請到足夠的空間。同時JVM會限制在抛出OutOfMemory錯誤之前在GC中花費的VM時間的比例。系統頻繁FGC大緻有五種情況:
- 記憶體洩漏
- 請求處理變慢導緻同時申請記憶體的線程太多
- metaspace 耗盡
- 常量池将堆區占滿
- 堆外記憶體耗盡
1w,正常情況下處理一個請求的時間是1ms,那同一時刻并行的請求數量僅為10。如果性能發生抖動,每個請求處理的時間增加到100ms,那同一時刻并行的請求數量就會增加到100個。每個線程在處理請求的時候都會new一些對象出來,長時間存活的線程會造成類似記憶體洩漏的效果,将系統的記憶體耗盡。同時fgc也會加劇系統性能的開銷,使系統變得更慢,産生雪崩。
三、如何讓系統fgc之後仍然能活下來
1.杜絕記憶體洩漏
記憶體洩漏造成系統癱瘓的頻率很高,有些系統定時從資料庫拉取配置資訊緩存到集合中,但是set不小心寫成了list,最終在新增元素的時候記憶體溢出了。養成良好的程式設計習慣,多關注些細節,就能避免很多未知的問題。
2.并發限制:防止系統被撐死
每台伺服器都有并行處理請求的上限,不管請求處理的多快,超過上限之後就會被撐死,對高并發的請求做好并發數限制是保持系統穩定的必要條件。需要注意的是,有一些系統在拒絕過多的請求時,也會做一些降級邏輯,降級邏輯也是有性能開銷的,同樣需要做并發限制,如果降級的請求超過并發限制,将不進行降級邏輯直接抛出異常。我們可使用的限流元件有很多,推薦我們阿裡自研的Sentinel 和 Netflix開源的Hystrix。
3.自适應限流:防止系統被摸死
我們需要自适應限流有兩個原因:
a. 每台伺服器所處的環境是不一樣的
有些伺服器和離線計算的vm混部在一起,有些部署在實體機,有些部署在新老型号的機器上,每台伺服器能承受的qps并不完全一樣。統一配置分布式系統中每台伺服器限流閥值,要麼發揮不出每台伺服器應有的作用,要麼在高qps的情況下一些比較慢的伺服器當機,是以用伺服器作為限流粒度是最合适的。
b.設定了正确的限流閥值,也可能被摸死
當單機承受的QPS 6~20倍于限流的流量時,拒絕一次請求的開銷就無法忽略不記了。譬如春晚活動有些系統設定了正确的限流也被6~20倍于限流的流量沖垮。這種死法稱為被摸死。應對這種情況,我們可以做的是在受到6~20倍的大流量時,動态減少限流的閥值。比如系統最開始接受1000qps,5000的拒絕流量過來會把系統摸死,這個時候我們調整系統的閥值,限流設定到100,被摸死的閥值就可以高一些,這樣就算有6000個請求進來,我們系統也可以保證活下來。
4.異常流量監控:防止長尾請求拖垮系統
我們盯系統監控的時候通常會關注99分位的資料,但如果設定了合理的限流,系統依然被流量打挂,就要從那百分之一的長尾資料入手了。有些長尾資料對系統的影響會非常大。想象如果一個put請求傳過來幾十兆的資料,對java是極為不友好的,很有可能産生fgc,讓請求變慢,導緻一系列問題。
總之,磨刀不誤砍柴工,當我們的系統因為fgc一次又一次重新開機的時候,不如花時間了解下系統産生性能問題的原因,将産生問題的那根針拔掉,晚上睡個安穩覺,白天更加充滿活力的挖新坑。希望每個程式員手裡都是一個穩定的系統。
參考資料:
jvm調優總結:
https://hllvm-group.iteye.com/group/wiki/?category_id=301諾亞(Noah)自适應限流 穩定性利器 :
https://www.atatech.org/articles/149208原文釋出時間:2019-11-27
作者:通木
本文來自雲栖社群合作夥伴“
阿裡技術”,了解相關資訊可以關注“
”。