總覽
有兩個很好的理由在可能的地方使用原語而不是包裝器。
- 明晰。 通過使用原語,您可以清楚地知道null值是不合適的。
- 性能。 使用原語通常更快。
清晰度通常比性能更重要,并且是使用它們的最佳理由。 但是,本文討論了使用包裝程序對性能的影響。
我對本文如何避免垃圾回收非常感興趣,但是缺少很多實際細節。 這是減少GC需求的系列文章中的第一篇。
使用包裝器的性能
以下微基準的行為與許多應用程序相同。
使用包裝器和包裝器集合循環
Map<Integer, Integer> counters = new HashMap<Integer, Integer>();
int runs = 20 * 1000;
for (Integer i = 0; i < runs; i++) {Integer x = i % 12;Integer y = i / 12 % 12;Integer times = x * y;Integer count = counters.get(times);if (count == null)counters.put(times, 1);elsecounters.put(times, count + 1);
}
這將為每個任務創建對象。 雖然將int用作循環計數器是一種常見的做法,但是使用Iterator也是一種常見的做法。您可以使用此微基準的類型和參數,但是您會獲得許多嘗試過的開發人員熟悉的內存配置文件調整他們的應用程序。 使用VisualVM,在五分鐘的時間內,堆使用情況看起來像這樣。

大約6分鐘內有20個次要GC。
每個循環的平均時間為亞微秒,非常快。
每個循環消耗4,099 ns
每個回路占用559 ns
每個循環消耗115 ns 每個回路占用240 ns 每個回路占用255 ns 在第一個測試中,JVM尚未預熱。
使用原語真的可以帶來很大的不同嗎?
使用原語的性能
以下基準測試與大多數應用程序的行為截然不同。 即使它所做的工作與先前的基準測試相同,也不會創建任何對象。
使用基元和數組循環
int[] counters = new int[144];
int runs = 20 * 1000;
for (int i = 0; i < runs; i++) {int x = i % 12;int y = i / 12 % 12;int times = x * y;counters[times]++;
}
堆的使用情況反映了這一點

5分鐘內沒有GC。 該測試可能運行了更長的時間,但仍未觸發GC。
而且每個循環的平均時間也要短得多
每個循環198 ns
每個循環占用17 ns
每個循環占用16 ns 每個循環占用14 ns 每個循環占用15 ns
在第一個測試中,JVM尚未預熱。
結論
使用基元會更好。 (除非有過多的裝箱和拆箱)
即使在性能不是很關鍵的應用程序中,它也可以提高代碼的清晰度,并且當您嘗試對應用程序進行概要分析時,它也可以減少“噪聲”級別,從而使問題更清晰。
筆記
即使在很少創建對象的測試中,您也可以看到一些對象分配。 這主要是由于VisualVM的輪詢。 為了減少這種情況,我將輪詢間隔從3秒更改為20秒。
使用-XX:NewSize = 100m增加Eden大小以使圖形更清晰(不建議使用此值(也許除了微基準測試)),但是它是您可能需要針對應用程序進行調整的參數。
完整代碼
- 原始基準
- 包裝基準
參考: Java中的低GC:使用原語,而不是來自Vanilla Java的 JCG合作伙伴 Peter Lawrey 的包裝 。
- 每個程序員都應該知道的事情
- 正確記錄應用程序的10個技巧
- 軟件設計法則
- Java最佳實踐系列
- 生存在狂野西部開發過程中的9條提示
- 如何在不到1ms的延遲內完成100K TPS
- 提升您的休眠引擎
翻譯自: https://www.javacodegeeks.com/2011/07/low-gc-in-java-use-primitives-instead.html