JVM 中最重要的一部分就是堆空間了,基本上大多數的線上 JVM 問題都是由於堆空間形成的 OutOfMemoryError。所以掌握 JVM 關於堆空間的參數配置對於排查線上問題很是重要。html
tips:本文全部配置,如無特別說明,均基於JDK1.8。java
咱們使用 -Xms 設置堆的初始空間大小,使用 -Xmx 設置堆的最大空間大小。jvm
java -Xms20m -Xmx30m GCDemo
在上面的命令中,咱們設置 JVM 的初始堆大小爲 20M,最大堆空間爲 30M。性能
在 JDK1.8 中,堆分爲年輕代和老年代。JVM 提供了參數 -Xmn 來設置年輕代內存的大小,但沒有提供參數設置老年代的大小。但其實老年代的大小就等於堆大小減去年輕代大小。學習
java -Xms20m -Xmn10M GCDemo
上面的命令中,咱們設置 JVM 堆初始大小爲20M。其中年輕代的大小爲 10M,那麼剩下的就是老年代的大小,有 10M了。 咱們能夠給上述命令加上-XX:+PrintGCDetails
參數來查看內存區域的分配信息。spa
如上圖所示,咱們能夠看到老年代的大小爲 10M。線程
在年輕代中,分爲三個區域,分別是:eden 空間、from 空間、to 空間。若是要設置這部分的大小,那麼就使用 -XX:SurvivorRatio 這個參數,該參數設置 eden / from 空間的比例關係,該參數的公式以下:日誌
-XX:SurvivorRatio = eden/from = eden/to
code
例如咱們的年輕代有 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。htm
下面咱們運行下命令來驗證一下。
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。
在 JDK 1.8 以前,所加載的類信息都放在永久代中。咱們用 -XX:PermSize 設置永久代初始大小,用 -XX:MaxPermSize 設置永久代最大大小。
java -XX:PermSize10m -XX:MaxPermSize50m -XX:+PrintGCDetails GCDemo
在上面的啓動參數中,咱們設置永久代初始大小爲 10M,最大大小爲 50M。咱們在 JDK1.7 的環境下運行上面的命令,會看到以下的 GC 日誌。
在上圖中,咱們能夠看到永久代的大小爲咱們設置的 10M。
在 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技術精選」公衆號,經過右下角菜單「入羣交流」加我好友,小助手會拉你入羣。