看板 Translate-CS 關於我們 聯絡資訊
原文網址:https://plumbr.eu/blog/how-to-estimate-memory-consumption 譯文網址:http://blog.dontcareabout.us/2014/03/blog-post.html BBS 用 markdown 語法撰寫 ______________________________________________________________________ 這個故事得從十年前開始說起, 那時我第一次接觸到一個[尖頭老闆]的問題: 「我們的產品要佈署的時候,需要買多好的 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 什麼時候達到穩定的狀態。 你會看到類似下面這張圖: ![](https://plumbr.eu/wp-content/uploads/2014/01/planning-capacity.png) 我們可以看到 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 每年要花公司 $4200。 轉換到 m1.small,每個 instance 只要 $520。 不要懷疑,當你要佈署的環境很大的時候, 你會無法忽視這 1 比 8 的成本差異。 結論 ---- 不幸的是,我仍然看到許多人做了如同十年前的我被迫做的決定。 這導致了過與不足的規劃,兩者同樣糟糕、尤其當你無法享有虛擬化優點的時候。 對我來說,我很幸運,但你可能沒辦法逃離你的客戶。 所以我只能建議用前面提到的簡單 framework 來未雨綢繆。 如果你喜歡這篇,我建議你關注我們在 Twitter 上的[效能調校]建議。 [尖頭老闆]: http://en.wikipedia.org/wiki/Pointy-haired_Boss [Java 效能優化指南]: http://www.amazon.com/ Java-Performance-Charlie-Hunt/dp/0137142528 [gcviewer]: http://www.tagtraum.com/gcviewer.html [效能調校]: https://twitter.com/intent/follow? region=follow_link&screen_name=javaplumbr -- 錢鍾書: 說出來的話 http://www.psmonkey.org 比不上不說出來的話 Java 版 cookcomic 版 只影射著說不出來的話 and more...... -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 220.129.132.145