在上一章中,我們了解了各種代GC。在本章中將討論如何調整GC。
堆大小是Java應用程式效能的重要因素。如果它太小,那麼它將經常被填充,因此,必須經常由GC收集。另一方面,如果只增加堆的大小,雖然它需要不那麼頻繁地收集,但是暫停的長度會增加。
此外,增加堆大小會對底層作業系統造成嚴重損害。使用分頁,作業系統使應用程式看到的記憶體比實際可用記憶體多得多。作業系統通過使用磁碟上的一些交換空間來管理它,將程式的非活動部分複製到其中。當需要這些部分時,OS會將它們從磁碟複製回記憶體。
假設一台機器有8G的記憶體,而JVM看到16G的虛擬記憶體,JVM不會知道系統上實際上只有8G可用。它只會從作業系統請求16G,一旦獲得該記憶體,它將繼續使用它。作業系統必須交換大量資料,這對系統造成了巨大的效能損失。
然後是在這種虛擬記憶體的完整GC期間發生的暫停。由於GC將在整個堆上進行收集和壓縮,因此必須等待很多時間才能將虛擬記憶體換出磁碟。在並行收集器的情況下,後台執行緒將不得不等待將資料從交換空間複製到記憶體。
所以這裡是應該如何決定最佳堆大小的問題。第一條規則是永遠不要求OS比實際存在的記憶體更多。這將完全防止頻繁交換的問題。如果機器安裝並執行了多個JVM,那麼它們所有組合的總記憶體請求小於系統中存在的實際RAM。
可以使用兩個標誌來控制JVM的記憶體請求大小 -
-XmsN
- 控制請求的初始記憶體。-XmxN
- 控制可以請求的最大記憶體。這兩個標誌的預設值取決於底層作業系統。例如,對於在MacOS上執行的64b JVM,-XmsN = 64M
,-XmxN =最小1G或總實體記憶體的1/4。
請注意,JVM可以自動在兩個值之間進行調整。例如,如果它注意到GC發生太多,只要它低於-XmxN
並且滿足所需的效能目標,它就會繼續增加記憶體大小。
如果您確切知道應用程式需要多少記憶體,則可以設定-XmsN = -XmxN
。在這種情況下,JVM不需要計算堆的「最佳」值,因此GC過程變得更有效。
可以決定要為YG分配多少堆,以及要為OG分配多少堆。這兩個值都以下列方式影響應用程式的效能。
如果YG的尺寸非常大,那麼它的收集頻率會降低。這將導致更少數量的物件被提升為OG。另一方面,如果過多地增加OG的大小,那麼收集和壓縮它會花費太多時間,這會導致長時間的STW暫停。因此,使用者必須在這兩個值之間找到平衡。
以下是可用於設定這些值的標誌 -
-XX:NewRatio = N
:YG與OG的比率(預設值= 2);-XX:NewSize = N
:YG的初始大小;-XX:MaxNewSize = N
:YG的最大尺寸;-XmnN
:使用此標誌將NewSize
和MaxNewSize
設定為相同的值;YG的初始大小由給定公式的NewRatio
值決定 -
(total heap size) / (newRatio + 1)
由於newRatio
的初始值為2
,因此上述公式將YG的初始值賦予總堆大小的1/3
。始終可以通過使用NewSize
標誌顯式指定YG的大小來覆蓋此值。此標誌沒有任何預設值,如果沒有明確設定,YG的大小將繼續使用上面的公式計算。
permagen
和元空間(Metaspace)是堆區域,JVM儲存類的後設資料。這個空間在Java 7中稱為「permagen」,在Java 8中,它被稱為「metaspace」。編譯器和執行時使用此資訊。
可以使用以下標誌控制permagen
的大小:-XX:PermSize = N
和-XX:MaxPermSize = N
。可以使用以下方法控制元空間的大小:-XX:Metaspace- Size = N
和-XX:MaxMetaspaceSize = N
。
在未設定標誌值時,如何管理permagen和元空間存在一些差異。預設情況下,兩者都具有預設的初始大小。但是,雖然元空間可以占用所需的堆,但permagen可以佔用不超過預設的初始值。例如,64b JVM具有82M的堆空間作為最大permagen大小。
請注意,由於元空間可以佔用無限量的記憶體,除非另有指定,否則可能會出現記憶體不足錯誤。每當調整這些區域的大小時,都會發生完整的GC。因此,在啟動期間,如果有許多類被載入,則元空間可以繼續調整大小,從而每次都生成一個完整的GC。因此,如果初始元空間大小太小,則需要花費大量時間來啟動大型應用程式。增加初始大小是一個好主意,因為它可以減少啟動時間。
儘管permagen和metaspace儲存了類後設資料,但它並不是永久性的,並且GC會回收空間,就像物件一樣。這通常用於伺服器應用程式。無論何時對伺服器進行新的部署,都必須清除舊的後設資料,因為新的類載入器現在需要空間。GC釋放了這個空間。