2014年3月23日 星期日

如何估算記憶體需求?

原文網址:https://plumbr.eu/blog/how-to-estimate-memory-consumption

這個故事得從十年前開始說起,那時我第一次接觸到一個尖頭老闆的問題:「我們的產品要佈署的時候,需要買多好的 server?」在產品發表之後,我們花了九個月的時間打造了一個金光閃閃的新系統,顯然公司已經承諾要提供整個解決方案,包含硬體的部份。

囧… 我麻煩大了。我只有短短幾年的經驗,回答這問題跟丟骰子沒啥兩樣。雖然我看起來整個就是缺乏信心,不過我仍然得生出一個答案。在 google 了四個小時後,眼花撩亂的腦袋裡仍然是相同的問題:

「如何估算所需的運算能力?」

在這篇文章中,我先用「如何估算記憶體需求」這個題目,讓你在寫 Java application 時有個初步的準則。如果你沒啥耐心看完,答案是先用「即時資料佔用的記憶體量」乘上 5 倍,然後以此作為微調起點。想知道背後的邏輯,那就繼續看下去,我會一步一步推導。

首先,我只能建議在取得詳細的資訊之前,避免回答這類的問題。你必定得根據效能需求來回答這類問題,所以在釐清這些東西之前甚至不應該開始。我說的也不是「這個系統要能同時承受 700 個使用者」這種曖昧的內容,而是大量特定的資訊,例如延遲時間、throughput、計算資料的數量、使用模式等等。而且還不要忘記預算這檔事。大家都會夢想有著次 ms 等級的延遲度,但沒有大財團等級的預算,那抱歉,它只會是一個幻想。

現在讓我們假設你已經有這些需求了。下一步是建立負載測試碼來模擬使用者行為。如果你現在能夠同時啟動這些程式碼,這就是你回答問題的基礎。As you might also have guessed, the next step involves our usual advice of measuring not guessing. But with caveat.

即時資料的大小

我們手上的「記憶體理想設定」任務,需要取得即時資料的大小。取得這個數據,我們就有微調的基準點。

如何定義「即時資料的大小」?Charlie Hunt 與 Binu John 在他們的《Java 效能優化指南》這本書中給了這樣的定義:

即時資料的大小,是 application 在穩定的狀態下,長時間存在的 object 所佔用的 heap 大小。

使用這個定義,我們就可以把 GC 的 log 功能開啟(-XX:+PrintGCTimeStamps -Xloggc:/tmp/gc.log -XX:+PrintGCDetails)來執行你的負載測試,然後把 log 紀錄給視覺化(例如用 gcviewer)來確定 application 什麼時候達到穩定的狀態。你會看到類似下面這張圖:

我們可以看到 minor GC 跟 full GC 都有作用,造成了一個常見的雙鋸齒圖形。這個特定的 application 似乎在 第 21 秒、第一次 full GC 後就到達了穩定的狀態。然則在大多數的情況下,需要 10~20 個 full GC 運作之後才能確定變化的趨勢。在經過 4 個 full GC 運作之後,我們推算即時資料的大小約為 100MB。

前頭提到的那本《Java 效能優化指南》指出:在一個典型的 Java EE application 中,即時資料大小與記憶體最佳化參數有很強的相關性。實際的證據也佐證了這個建議:

heap 的極大值設定為即時資料大小的 3~4 倍。

所以,就我們手上的 application 初步測試後,我們應該將 -Xmx 設定在 300m~400m 之間。

我們結合了書上給的其他意見,建議 permanent generation 的最大值設為「permanent generation 的即時資料大小」的 1.2~1.5 倍,而 -XX:NewRation 的設定為「即時資料大小」的 1~1.5 倍。我們正在收集更多的資料以確定是否存在正相關,但在那之前,我會建議你以目前 survival 與 eden 的設定結果為基礎,監看你的 allocation rate。

你可能會問為什麼要這樣?事實上,有兩個不是那麼表面的原因:

  • 寫這篇文章時,8G 的記憶體售價在 $100 左右。
  • 虛擬化、尤其是用 Amazon AWS 這種大型供應商的服務,調整容量是十分容易的。

這兩個原因都部份成立,而且絕對會減少配置上的需求。不過這兩點仍然沒有讓你脫離危險地帶:

  • 當弄了大量的記憶體,你非常有可能影響延遲時間。當 heap 有 8G 或更多的時候,full GC 很容易造成幾十秒的停頓。
  • 當過度配置隨之而來的「之後再調整」心態,常常「之後」就再也沒有然後了。我曾面對許多 application 在遠超過配置的環境上執行,只是因為想「之後再調整」。 例如前面提到的 application,我發現在 Amazon EC2 m1.xlarge instance 上,每個 instance 每年要花公司 4200m1.smallinstance520。不要懷疑,當你要佈署的環境很大的時候,你會無法忽視這 1 比 8 的成本差異。

結論

不幸的是,我仍然看到許多人做了如同十年前的我被迫做的決定。這導致了過與不足的規劃,兩者同樣糟糕、尤其當你無法享有虛擬化優點的時候。

對我來說,我很幸運,但你可能沒辦法逃離你的客戶。所以我只能建議用前面提到的簡單 framework 來未雨綢繆。如果你喜歡這篇,我建議你關注我們在 Twitter 上的效能調校建議。

沒有留言:

張貼留言