1. 程式人生 > >8年開發架構師淺析SpringBoot的JVM的內存占用與Docker-spring.io

8年開發架構師淺析SpringBoot的JVM的內存占用與Docker-spring.io

png ots 處理 微服務 大小 同時 例如 jvm的內存 let

技術分享圖片

JVM可能是一個復雜的野獸。值得慶幸的是,大部分復雜性都在幕後,我們作為應用程序開發人員和部署人員通常不必過於擔心。隨著基於容器的部署策略的興起,需要引起註意的一個復雜領域是JVM的內存占用。

兩種內存

JVM將其內存分為兩大類:堆內存和非堆內存。堆內存是人們通常最熟悉的部分。它是存儲由應用程序創建的對象的位置。它們一直存在,直到它們不再被引用並被垃圾收集。通常,應用程序使用的堆量將根據當前負載而波動。

JVM的非堆內存分為幾個不同的區域。我們可以使用HotSpot VM的本機內存跟蹤(NMT)來檢查這些區域的內存使用情況。請註意,雖然NMT不跟蹤所有原生本機Native內存使用情況(例如,它不跟蹤第三方本機代碼內存分配),但對於大類典型的Spring應用程序來說已經足夠了。可以通過啟動應用程序-XX:NativeMemoryTracking=summary然後使用jcmd <pid> VM.native_memory summary來顯示內存使用情況摘要來使用NMT 。

讓我們通過查看應用程序來說明NMT的使用,在這種情況下,我們的老朋友Petclinic。下面的餅圖顯示了當使用48MB最大堆(-Xmx48M)啟動Petclinic時由NMT報告的JVM的內存使用量(減去其自身的開銷):

技術分享圖片

正如您所看到的,非堆內存占絕大多數JVM的內存使用量,堆內存僅占總數的六分之一。在這種情況下,大約44MB(垃圾收集後立即使用33MB)。非堆內存使用總量為223MB。

本機Native內存區域

  • 壓縮類空間:用於存儲有關已加載的類的信息。受到約束MaxMetaspaceSize。已加載的類數的函數。
  • 線程:JVM中線程使用的內存。正在運行的線程數的函數。
  • 代碼緩存:JIT用於存儲其輸出的內存。已加載的類數的函數。受到約束ReservedCodeCacheSize。可以通過調整JIT來減少,例如,禁用分層編譯。
  • GC:存儲GC使用的數據。根據使用的垃圾收集器而有所不同。
  • 符號:存儲符號,如字段名稱,方法簽名和實習字符串。過多的符號內存使用情況可能表明字符串過於激進。
  • 內部:存儲不適合任何其他區域的其他內部數據。

非堆內存與堆內存的不同

與堆內存相比,非堆內存在負載下不太可能發生變化。一旦應用程序加載了它將使用的所有類並且JIT完全預熱,事情就會陷入穩定狀態。要查看壓縮類空間使用量的減少,加載類的類加載器需要進行垃圾回收。在將應用程序部署到servlet容器或應用程序服務器時,這種情況更常見 - 應用程序的類加載器將在取消部署應用程序時進行垃圾收集 - 但現代應用程序部署方法很少發生。

調整JVM的大小

配置JVM以有效利用給定數量的可用RAM並不容易。如果您啟動JVM -Xmx16M並期望它最多可以使用16MB的RAM,那麽您會感到非常驚訝。

調整JVM大小的一個有趣的方面是JIT的代碼緩存。默認情況下,HotSpot JVM最多可使用240MB。如果代碼緩存太小,JIT將耗盡空間來存儲其輸出,因此性能將受到影響。如果緩存太大,可能會浪費內存。在調整代碼緩存大小時,查看應用程序內存使用情況及其性能的影響非常重要。

在Docker容器中運行時,Java的最新版本現在知道容器的內存限制並嘗試相應地調整JVM的大小。不幸的是,這種大小調整經常過度分配非堆內存並且分配不足。假設您有一個在具有2個CPU和512MB可用內存的容器中運行的應用程序。您希望它能夠處理更多負載,因此您將CPU加倍為4,將內存加倍至1GB。如上所述,堆使用通常根據負載而變化,而非堆使用則更少。因此,我們希望將大部分額外的512MB內存提供給堆來應對增加的負載。不幸的是,JVM默認情況下不會這樣做,並且會在其堆和非堆區域之間更均等地分配額外的內存。

值得慶幸的是,CloudFoundry團隊擁有豐富的JVM內存占用知識。如果您要將應用程序推送到CloudFoundry,構建包將自動為您應用此知識。

這對Spring來說意味著什麽?

考慮到堆和非堆內存使用情況,我們花了很多時間在Spring團隊中考慮性能和內存利用率。限制非堆內存使用的一種方法是使Framework的一部分盡可能通用。這方面的一個例子是使用反射來創建應用程序的bean並將其註入到應用程序的bean中。由於使用了反射,所使用的Framework代碼量保持不變,無論應用程序包含多少bean。我們使用基於堆的緩存來優化啟動時間,一旦啟動完成就清除此緩存。然後,垃圾收集器可以輕松地回收堆內存,從而在應用程序處理其工作負載時為應用程序提供盡可能多的內存。

Java程序員註意啦,我們整理了一個微服務的架構專題,專門針對Docker、Spring Cloud、Dubbo、Spring Boot等技術,同時也給大家搜集一些學習資料和視頻,希望能夠幫助到家。

8年開發架構師淺析SpringBoot的JVM的內存占用與Docker-spring.io