跑的好好的 Java 進程,怎麼突然就癱瘓了

內存回收一直是 Java的痛點

用 Java 無法做出類似 Redis 這樣的產品。Java 的內存回收機制使我們在編寫代碼時不需要關注對象的回收,同時加大了內存回收的消耗,標記複製需要做內存拷貝,標記清除算法則需要 stop the world 。所以我們在使用緩存的時候,量稍微大一些就需要藉助類似 Redis 這樣的中間件幫我們處理了。作爲 Javaer ,我們享受了自動內存回收的安逸,同時也需要多瞭解下內存優化的方法。

爲什麼 FGC 停不下來了

什麼情況下會 GC

爲了瞭解我們的系統爲什麼會不停 FGC ,我們需要先了解一下系統什麼情況下會 GC 。在 Jvm 層面,當我們 new 一個對象的時候, Jvm 會先在堆區分配對象需要的內存,這個時候如果內存不夠的話,就需要 GC 了, GC 的返回結果就是對象的空間地址。Jvm 會先進行 ygc ,也就是我們通常說的標記複製,如果 ygc 之後依然申請不到空間,就會進行 FGC 了。同理,如果 FGC 之後依然沒有足夠的空間,就會循環的進行 FGC ,直到申請到足夠的空間。

導致不停的 FGC 的原因

如上文所講, FGC 有可能發生在你的每一行代碼。如果 FGC 之後依然沒有足夠的空間,就會不停的 FGC ,直到申請到足夠的空間。同時 JVM 會限制在拋出 OutOfMemory 錯誤之前在 GC 中花費的 VM 時間的比例。系統頻繁 F 大致有五種情況:

  • 內存泄漏
  • 請求處理變慢導致同時申請內存的線程太多
  • metaspace 耗盡
  • 常量池將堆區佔滿
  • 堆外內存耗盡

在一個高併發的系統中,多數 FGC 是請求處理變慢導致的。假設單機承受 tps 是1w,正常情況下處理一個請求的時間是 1ms ,那同一時刻並行的請求數量僅爲 10 。如果性能發生抖動,每個請求處理的時間增加到 100ms ,那同一時刻並行的請求數量就會增加到 100 個。每個線程在處理請求的時候都會 new 一些對象出來,長時間存活的線程會造成類似內存泄漏的效果,將系統的內存耗盡。同時 FGC 也會加劇系統性能的開銷,使系統變得更慢,產生雪崩。

如何讓系統 FGC 之後仍然能活下來

杜絕內存泄漏

內存泄漏產生的原因以及解決辦法網上有很多資料,這裏就不寫了。內存泄漏造成系統癱瘓的頻率很高,有些系統定時從數據庫拉取配置信息緩存到集合中,但是 set 不小心寫成了 list ,最終在新增元素的時候內存溢出了。養成良好的編程習慣,多關注些細節,就能避免很多未知的問題。

併發限制:防止系統被撐死

每臺服務器都有並行處理請求的上限,不管請求處理的多快,超過上限之後就會被撐死,對高併發的請求做好併發數限制是保持系統穩定的必要條件。需要注意的是,有一些系統在拒絕過多的請求時,也會做一些降級邏輯,降級邏輯也是有性能開銷的,同樣需要做併發限制,如果降級的請求超過併發限制,將不進行降級邏輯直接拋出異常。

自適應限流:防止系統被摸死

我們需要自適應限流有兩個原因:

每臺服務器所處的環境是不一樣的

有些服務器和離線計算的 vm 混部在一起,有些部署在實體機,有些部署在新老型號的機器上,每臺服務器能承受的 qps 並不完全一樣。統一配置分佈式系統中每臺服務器限流閥值,要麼發揮不出每臺服務器應有的作用,要麼在高 qps 的情況下一些比較慢的服務器宕機,所以用服務器作爲限流粒度是最合適的。

設置了正確的限流閥值,也可能被摸死

當單機承受的 QPS 6~20 倍於限流的流量時,拒絕一次請求的開銷就無法忽略不記了。譬如春晚活動有些系統設置了正確的限流也被 6~20 倍於限流的流量沖垮。這種死法稱爲被摸死。應對這種情況,我們可以做的是在受到 6~20 倍的大流量時,動態減少限流的閥值。比如系統最開始接受 1000qps ,5000 的拒絕流量過來會把系統摸死,這個時候我們調整系統的閥值,限流設置到 100 ,被摸死的閥值就可以高一些,這樣就算有 6000 個請求進來,我們系統也可以保證活下來。

阿里有結合算法動態調整單機限流閥的產品,已經對外公佈了,感興趣的同學可以搜一下淘系技術公衆號中的 諾亞自適應限流 的相關內容。

異常流量監控:防止長尾請求拖垮系統

我們盯系統監控的時候通常會關注 99 分位的數據,但如果設置了合理的限流,系統依然被流量打掛,就要從那百分之一的長尾數據入手了。有些長尾數據對系統的影響會非常大。想象如果一個 put 請求傳過來幾十兆的數據,對 Java 是極爲不友好的,很有可能產生 FGC ,讓請求變慢,導致一系列問題。

總之,磨刀不誤砍柴工,當我們的系統因爲 FGC 一次又一次重啓的時候,不如花時間瞭解下系統產生性能問題的原因,將產生問題的那根針拔掉,晚上睡個安穩覺,白天更加充滿活力的挖新坑。希望每個程序員手裏都是一個穩定的系統。

作者信息:通木, Github 賬號 zhdd99 ,阿里巴巴基礎設施事業部高級開發工程師,目前主要負責阿里巴巴IDC監控系統。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章