javascript
Spring Boot应用程序浪费了内存
內存是當今世界上被廣泛浪費的資源之一。 由于編程效率低下,令人吃驚的(有時是“令人震驚的”)內存浪費被浪費了。 我們看到這種模式在多個企業應用程序中重復出現。 為了證明這種情況,我們進行了一項小型研究。 我們分析了著名的spring boot pet診所應用程序,以查看浪費了多少內存。 該應用程序是由社區設計的,旨在顯示spring應用程序框架如何用于構建簡單但功能強大的面向數據庫的應用程序。
環境
- Spring Boot 2.1.4。發布
- Java SDK 1.8
- 的Tomcat 8.5.20
- 帶有MySQL Connector / J 8.0.15的MySQL 5.7.26
壓力測試
我們使用了流行的開源負載測試工具Apache JMeter進行壓力測試。 我們使用以下設置執行了30分鐘的負載測試:
- 線程數(用戶)– 1000(連接到目標的用戶數)
- 加速周期(以秒為單位)– 10.所有請求開始的時間范圍。 根據我們的配置,每0.01秒,將啟動1個新線程,即100個線程/秒。
- 循環計數–永遠。 這1000個線程背對背執行測試迭代。
- 持續時間(秒)-1800。 加速后,1000個線程連續運行1800秒。
我們在負載測試中采用了以下方案: ?
- 將新的寵物主人添加到系統。
- 查看有關寵物主人的信息。
- 向系統添加新寵物。
- 查看有關寵物的信息。
- 將有關訪問的信息添加到寵物的訪問歷史中。
- 更新有關寵物的信息。
- 更新有關寵物主人的信息。
- 通過搜索其姓名查看所有者信息。
- 查看所有所有者的信息。
如何測量內存浪費?
工業界有數百種工具可以顯示所使用的內存量 。 但是我們很少遇到能夠測量由于編程效率低下而浪費的內存量的工具。 HeapHero是一個簡單的工具,可以分析堆轉儲并告訴您由于編程效率低而浪費了多少內存。
測試運行時,我們從Spring Boot Pet Clinic應用程序捕獲了堆轉儲。 (有7種不同的選項可從Java / Android應用程序 捕獲堆轉儲 。您可以選擇最方便的選項)。
我們將捕獲的堆轉儲上傳到HeapHero工具中。 工具生成了這個漂亮的報告,顯示由于效率低下的編程浪費了65%的內存。 是的,這是一個簡單的原始應用程序,應該在其中實現了所有最佳實踐,在一個著名的框架上也浪費了65%的內存。
圖:由HeapHero生成的圖表,顯示65%的內存被Spring Boot寵物診所應用程序浪費了分析內存浪費
從報告中,您可以注意到以下內容:
- 字符串重復導致浪費了15.6%的內存
- 由于原始數組效率低下,浪費了14.6%的內存
- 由于重復的原始數組浪費了14.3%的內存
- 由于收集效率低下,浪費了12.1%的內存
在此Spring啟動應用程序 (和大多數企業應用程序)中浪費內存的主要原因是字符串重復。 該報告顯示了由于重復字符串而浪費了多少內存,它們是什么字符串,誰在創建它們以及如何對其進行優化。
無花果:重復的字符串您會注意到由于重復的字符串浪費了15.6%的內存。 請注意
- 'Goldi'字符串已被創建207,481次。
- “訪問”字符串已創建132,308次。 “訪問”是我們在測試腳本中提到的描述。
- “班加羅爾”字符串已創建75,374次。 “ Banglore”是我們在測試腳本中指定的城市名稱。
- '123123123'已被創建37,687次。
- “ Mahesh”字符串已創建37,687次。
顯然,“ Goldi”是通過測試腳本在屏幕上輸入的寵物的名稱。 “訪問”是通過測試腳本在屏幕上輸入的描述。 同樣,是值。 但是有一個問題,為什么要創建相同的字符串對象那么幾千次。
我們都知道字符串是不可變的(即一旦創建,就無法修改)。 鑒于為什么要創建成千上萬個重復的字符串?
HeapHero工具還報告創建這些重復字符串的代碼路徑。
無花果:重復字符串源自的代碼路徑這是修復應用程序中重復字符串的高級建議 。 您可以采用適用于您的應用程序的策略。
在彈簧靴寵物診所應用中造成內存浪費的另一個主要原因是收集效率低下。 以下是HeapHero報告的摘錄:
圖:由于收集效率低而浪費的內存您會注意到,內存中99%的LinkedHashSet中沒有任何元素。 如果沒有元素,為什么還要創建LinkedHashSet? 當您創建一個新的LinkedHashSet對象時,將在內存中保留16個元素的空間。 現在為這16個元素保留的所有空間都被浪費了。 如果對LinedHashset進行延遲初始化,則不會出現此問題。
不良做法:
private LinkedHashSet<String, String>myHashSet = new LinkedHashSet(); public void addData(String key, String value) { myHashSet.put(key, value); }最佳實踐:
private LinkedHashSet<String, String>myHashSet; public void addData(String key, String value) { If (myHashSet == null ) { myHashSet = new LinkedHashSet(); } myHashSet.put(key, value); }同樣,另一個觀察結果是:68%的ArrayList中僅包含1個元素。 創建ArrayList對象時,將在內存中保留10個元素的空間。 這意味著在88%的ArrayList中9個元素的空間被浪費了。 如果可以使用容量初始化ArrayList,則可以避免此問題。
不良做法:使用默認值初始化集合。
new ArrayList();最佳實踐:使用容量初始化集合
new ArrayList( 1 );內存不便宜
一個人可以反駁說,內存是如此便宜,那么為什么我要擔心它呢? 公平的問題。 但是在云計算時代,我朋友的記憶并不便宜。 有4種主要的計算資源:
您的應用程序可能在AWS EC2實例上運行的數十萬個應用程序服務器上運行。 在上述4種計算資源中,哪個資源在EC2實例中已飽和? 我要求您在這里稍等一下,然后再繼續閱讀。 考慮一下,首先確定哪些資源已飽和。
對于大多數應用程序,它是*內存*。 CPU始終為30 – 60%。 總是有大量的存儲空間。 很難飽和網絡(除非您的應用程序正在流式傳輸大量視頻內容)。 因此,對于大多數應用程序來說,首先是內存飽和。 即使CPU,存儲和網絡未充分利用,僅由于內存變得飽和,您最終還是會配置越來越多的EC2實例。 這將使您的計算成本增加幾倍。
另一方面,由于編程效率低下,現代應用程序無一例外地浪費了30%-90%的內存。 即使在沒有太多業務邏輯的Spring Boot寵物診所之上,也浪費了65%的內存。 實際的企業應用程序將浪費相似的數量,甚至更多。 因此,如果您可以編寫內存有效的代碼,那么它將降低您的計算成本。 由于內存是第一個達到飽和的資源,因此,如果可以減少內存消耗,則可以在較少數量的服務器實例上運行應用程序。 您也許可以減少30 – 40%的服務器。 這意味著您的管理層可以減少30%-40%的數據中心(或云托管提供商)成本,再加上維護和支持成本。 它可以節省數百萬/數十億美元的成本。
結論
除了減少計算成本,編寫內存效率高的代碼后,您的客戶體驗也將變得更好。 如果您可以減少為服務新的傳入請求而創建的對象數量,則響應時間將大大縮短。 由于創建的對象較少,因此在創建和垃圾回收對象上將花費較少的CPU周期。 減少響應時間將提供更好的客戶體驗。
翻譯自: https://www.javacodegeeks.com/2019/11/memory-wasted-by-spring-boot-application.html
總結
以上是生活随笔為你收集整理的Spring Boot应用程序浪费了内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 党员备案是什么意思(党员备案吗)
- 下一篇: activemq网络桥接_ActiveM