JVM基礎系列第11講:JVM參數之堆棧空間配置


JVM 中最重要的一部分就是堆空間了,基本上大多數的線上 JVM 問題都是因為堆空間造成的 OutOfMemoryError。因此掌握 JVM 關於堆空間的參數配置對於排查線上問題非常重要。

tips:本文所有配置,如無特別說明,均基於JDK1.8。

堆配置

我們使用 -Xms 設置堆的初始空間大小,使用 -Xmx 設置堆的最大空間大小。

java -Xms20m -Xmx30m GCDemo

在上面的命令中,我們設置 JVM 的初始堆大小為 20M,最大堆空間為 30M。

年輕代

在 JDK1.8 中,堆分為年輕代和老年代。JVM 提供了參數 -Xmn 來設置年輕代內存的大小,但沒有提供參數設置老年代的大小。但其實老年代的大小就等於堆大小減去年輕代大小。

java -Xms20m -Xmn10M GCDemo

上面的命令中,我們設置 JVM 堆初始大小為20M。其中年輕代的大小為 10M,那么剩下的就是老年代的大小,有 10M了。 我們可以給上述命令加上-XX:+PrintGCDetails 參數來查看內存區域的分配信息。

如上圖所示,我們可以看到老年代的大小為 10M。

Eden區

在年輕代中,分為三個區域,分別是:eden 空間、from 空間、to 空間。如果要設置這部分的大小,那么就使用 -XX:SurvivorRatio 這個參數,該參數設置 eden / from 空間的比例關系,該參數的公式如下:

-XX:SurvivorRatio = eden/from = eden/to

例如我們的年輕代有 10 M,而我們設置 -XX:SurvivorRatio 參數為 2。也就是說 eden / from = eden / to = 2。這里教一個快速計算的方法,我們假設 eden = 2,那么 from = 1,to = 1,那么 eden + from + to = 10M。這樣就可以算出每一份大小是 10/4 = 2.5M。所以 Eden 區 = 2.5 * 2 = 5M,from 區是 2.5 M,to 區是 2.5 M。

下面我們運行下命令來驗證一下。

java -Xms20m -Xmn10M -XX:SurvivorRatio=2 -XX:+PrintGCDetails GCDemo

在上面的啟動參數中,我們設置堆初始大小為 20M,年輕代大小為 10M,年輕代的 SurvivorRatio 比例為 2。那么最終分配的結果將會是:年輕代 10M,其中 Eden 區 5M、From 區 2.5M、To 區 2.5 M,老年代 10M。

從上圖可以看到:eden 空間是 5120 K,from 和 to 空間是 2560 K。

上圖還有一個細節,即 PSYoungGen 這里的 total 只有 7680K,難道年輕代只有 7.5M 的內存嗎?為什么不是 10M 呢?其實是因為這里的 total 指的是可用內存,from space 和 to space 兩個區域,同一時間只有一個區域是可以用的。所以可用內存是 5120 + 2560 = 7680。

永久代(JDK1.7)

在 JDK 1.8 之前,所加載的類信息都放在永久代中。我們用 -XX:PermSize 設置永久代初始大小,用 -XX:MaxPermSize 設置永久代最大大小。

java -XX:PermSize10m -XX:MaxPermSize50m -XX:+PrintGCDetails GCDemo

在上面的啟動參數中,我們設置永久代初始大小為 10M,最大大小為 50M。我們在 JDK1.7 的環境下運行上面的命令,會看到如下的 GC 日志。

在上圖中,我們可以看到永久代的大小為我們設置的 10M。

元空間(JDK1.8)

在 JDK 1.8 之前,所有加載的類信息都放在永久代中。但在 JDK1.8 之時,永久代被移除,取而代之的是元空間(Metaspace)。在元空間這塊內存中,有兩個參數很相似,它們是: -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize。

java -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=50m -XX:+PrintGCDetails GCDemo

上面的命令中,我們設置 MetaspaceSize 為 10M,MaxMetaspaceSize 為 50M。但其實它們並不是設置初始大小和最大大小的。

從上面的執行結果可以看到,Metaspace 空間的大小為 2.6M 左右,並不是我們設置的 10M。那是因為 MetaspaceSize 設置的是元空間發生 GC 的初始閾值。當達到這個值時,元空間發生 GC 操作,這個值默認是 20.8M。而 MaxMetaspaceSize 則是設置元空間的最大大小,默認基本是機器的物理內存大小。雖然可以不設置,但還是建議設置一下,因為如果一直不斷膨脹,那么 JVM 進程可能會被 OS kill 掉。

棧空間

棧空間是每個線程各自有的一塊區域,如果棧空間太小,也會導致 StackOverFlow 異常。而要設置棧空間大小,只需要使用 -Xss 參數就可以。

java -Xss2m GCDemo

上面的啟動命令設置最大棧空間為 2M。

直接內存

在 JVM 中還有一塊內存,它獨立於 JVM 的堆內存,它就是:直接內存。我們可以使用 -XX:MaxDirectMemorySize 設置最大直接內存。如果不設置,默認為最大堆空間,即 -Xmx。

java -XX:MaxDirectMemorySize=50m GCDemo

上面的啟動命令設置直接內存最大值為 50M。

當直接內存使用達到設置值時,就會觸發垃圾回收。如果不能有效釋放足夠空間,就會引發直接內存溢出導致系統的 OOM。

總結

參數 含義
-Xms 初始堆大小
-Xmx 最大堆空間
-Xmn 設置新生代大小
-XX:SurvivorRatio 設置新生代eden空間和from/to空間的比例關系
-XX:PermSize 方法區初始大小
-XX:MaxPermSize 方法區最大大小
-XX:MetaspaceSize 元空間GC閾值(JDK1.8)
-XX:MaxMetaspaceSize 最大元空間大小(JDK1.8)
-Xss 棧大小
-XX:MaxDirectMemorySize 直接內存大小,默認為最大堆空間

參考資料


如果只是看,其實無法真正學會知識的。為了幫助大家更好地學習,我建了一個虛擬機群,專門討論學習 Java 虛擬機方面的內容,每周針對我所發文章進行討論答疑。如果你有興趣,關注「Java技術精選」公眾號,通過右下角菜單「入群交流」加我好友,小助手會拉你入群。


JVM系列目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM