在上一章中,我們瞭解了各種代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
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釋放了這個空間。