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 直接內存大小,默認爲最大堆空間

http://lovestblog.cn/blog/2016/10/29/metaspace/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章