所以當評估是否真的有必要加入緩存時,應該先計劃收集一些基本的使用統計信息(比如命中率和未命中率等),以此證明緩存層帶來的真正價值。
6.所有應用都需要關注Stop-The-World問題
Java平臺存在一個無法改變的事實:為運行垃圾收集,所有應用線程必須周期性停頓。有時這被當作Java的一個嚴重缺點,即使沒有任何真憑實據。
實證研究表明,如果數字數據(如價格波動)變化的頻率超過200毫秒一次,人就無法正常感知了。
應用主要是給人用的,因此我們有一個有用的經驗法則,200毫秒或低于200毫秒的Stop-The-World(STW)通常是沒有影響的。有些應用可能有更高的要求(如流媒體),但很多GUI應用是不需要的。
少數應用(比如低延遲交易或機械控制系統)無法接受200毫秒的停頓。除非編寫的就是這類應用,否則用戶基本感覺不到垃圾收集器的影響。
值得一提的是,在應用線程數量超過物理核數的任何系統中,操作系統必須控制對CPU的分時訪問。Stop-The-World聽著可怕,但實際上任何應用(不管是JVM還是其他應用)都要面對稀缺計算資源的爭用問題。
如果不去測量,JVM對應用性能有何附加影響是不清楚的。
總之,請打開GC日志,以此來確定停頓時間是否真的影響了應用。通過分析日志來確定停頓時間,這里既可以手工分析,也可以利用腳本或工具分析。然后再判定它們是否真的給應用于帶來了問題。最重要的是,問自己一個關鍵的問題:確實有用戶抱怨嗎?
7.手寫對象池適合一大類應用
認為Stop-The-World停頓在某種程度上是不好的,應用開發團隊的一個常見反應就是在Java堆內實現自己的內存管理技術。這往往會歸結為實現一個對象池(甚至是全面的引用計數),而且需要使用了領域對象的任何代碼都參與進來。
這種技術幾乎總是具有誤導性的。它基于過去的認知,那時對象分配非常昂貴,而修改對象則廉價的多?,F在的情況已經完全不同了。
現在的硬件在分配時非常高效;最新的桌面或服務器硬件,內存帶寬至少是2到3GB。這是一個很大的數字,除非專門編寫的應用,否則要充分利用這么大的帶寬還真不容易。
一般來說,正確實現對象池非常困難(尤其是有多個線程工作時),而且對象池還帶來了一些負面的要求,使這種技術不是一個通用的良好選擇:
所有接觸到對象池代碼的開發者必須了解對象池,而且能正確處理
哪些代碼知道對象池,哪些代碼不知道對象池,其界限必須讓大家知道,并且寫在文檔中
這些額外的復雜性要保持更新,而且定期復審
如果有一條不滿足,悄然出現問題(類似于C 中的指針復用)的風險就又回來了
總之,只有GC停頓不能接受,而且調校和重構也未能將停頓減小到可以接受的水平時,才能使用對象池。
8.在垃圾收集中,相對于Parallel Old,CMS總是更好的選擇
Oracle JDK默認使用一個并行的Stop-The-World收集器來收集老年代,即Parallel Old收集器。
Concurrent-Mark-Sweep (CMS)是一個備選方案,在大部分垃圾收集周期,它允許應用線程繼續運行,但這是有代價的,而且有一些注意事項。
允許應用線程與垃圾收集線程一起運行,不可避免地帶來一個問題:應用線程修改了對象圖,可能會影響對象的存活性。這種情況必須在事后加以清理,因此CMS實際上有兩個STW階段(通常非常短)。
這會帶來一些后果:
必須將所有應用線程帶到安全點,每次Full GC期間會停頓兩次;
盡管垃圾收集與應用同時執行,但應用的吞吐量會降低(通常是50%);
在使用CMS進行垃圾收集時,JVM所用的簿記信息(和CPU周期)遠高于其他的并行收集器。
這些代價是不是物有所值,取決于應用的情況。但是天下沒有免費的午餐。CMS收集器在設計上值得稱道,但它不是萬能的。
所以在確定CMS是正確的垃圾收集策略之前,首先應該確認Parallel Old的STW停頓確實不能接受,而且已經無法調校。最后,我重點強調一下,所有指標必須從與生產系統等價的系統中獲得。
9.增加堆的大小可以解決內存問題
當應用陷入困境,并且懷疑是GC的問題時,很多應用團隊的反應就是增加堆的大小。在某些情況下,這樣做可以快速見效,而且為我們留出了時間來考慮更周詳的解決方案。然而,如果沒有充分理解性能問題的原因,這種策略反而會讓事情變得更糟糕。
考慮一個編碼非常糟糕的應用程序,它正在產生很多領域對象 (它們的生存時間很有代表性,比如說是2-3秒)。如果分配率高到一定程度,垃圾收集會頻繁進行,這樣領域對象會被提升到老年代。領域對象幾乎是一進入年老代,生存時間就結束了,從而直接死亡,但它們直到下一次Full GC時才會被回收。
如果增加了應用的堆大小,我們所做的不過是增加了相對短命的對象進入和死亡所用的空間。這會導致Stop-The-World停頓時間更長,對應用并無益處。
在修改堆大小或者調校其他參數之前,理解對象的分配和生存時間的動態是很有必要的。沒有測量性能數據就盲目行動,只會使情況更糟糕。在這里,垃圾收集器的老年代分布情況特別重要。
結論
當談到Java的性能調校時,直覺常常起誤導作用。我們需要實驗數據和工具來幫助我們將平臺的行為可視化并加強理解。
垃圾收集就是最好的例子。對于調?;蛘呱芍笇д{校的數據而言,GC子系統擁有無限的潛力;但是對于產品應用而言,不使用工具很難理解所產生數據的意義。
默認情況下,運行任意Java進程(包括開發環境和產品環境),應該至少總是使用如下參數:
-verbose:gc(打印GC日志)
-Xloggc:(更全面的GC日志)
-XX:+PrintGCDetails(更詳細的輸出)
-XX:+PrintTenuringDistribution(顯示JVM所使用的將對象提升進入老年代的年齡閾值)
然后使用工具來分析日志,這里可以利用手寫的腳本,可以用圖生成,還可以使用GCViewer(開源的)或jClarity Censum這樣的可視化工具。
原文轉自:http://www.infoq.com/cn/articles/9_Fallacies_Java_Performance