用 Java 沒法作出相似 Redis 這樣的產品。Java 的內存回收機制使咱們在編寫代碼時不須要關注對象的回收,同時加大了內存回收的消耗,標記複製須要作內存拷貝,標記清除算法則須要 stop the world 。因此咱們在使用緩存的時候,量稍微大一些就須要藉助相似 Redis 這樣的中間件幫咱們處理了。做爲 Javaer ,咱們享受了自動內存回收的安逸,同時也須要多瞭解下內存優化的方法。程序員
爲了瞭解咱們的系統爲何會不停 FGC ,咱們須要先了解一下系統什麼狀況下會 GC 。在 Jvm 層面,當咱們 new 一個對象的時候, Jvm 會先在堆區分配對象須要的內存,這個時候若是內存不夠的話,就須要 GC 了, GC 的返回結果就是對象的空間地址。Jvm 會先進行 ygc ,也就是咱們一般說的標記複製,若是 ygc 以後依然申請不到空間,就會進行 FGC 了。同理,若是 FGC 以後依然沒有足夠的空間,就會循環的進行 FGC ,直到申請到足夠的空間。算法
如上文所講, FGC 有可能發生在你的每一行代碼。若是 FGC 以後依然沒有足夠的空間,就會不停的 FGC ,直到申請到足夠的空間。同時 JVM 會限制在拋出 OutOfMemory 錯誤以前在 GC 中花費的 VM 時間的比例。系統頻繁 F 大體有五種狀況:數據庫
在一個高併發的系統中,多數 FGC 是請求處理變慢致使的。假設單機承受 tps 是1w,正常狀況下處理一個請求的時間是 1ms ,那同一時刻並行的請求數量僅爲 10 。若是性能發生抖動,每一個請求處理的時間增長到 100ms ,那同一時刻並行的請求數量就會增長到 100 個。每一個線程在處理請求的時候都會 new 一些對象出來,長時間存活的線程會形成相似內存泄漏的效果,將系統的內存耗盡。同時 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監控系統。
本文做者:通木
本文爲阿里雲內容,未經容許不得轉載。