1. 程式人生 > >JVM 調優 —— 新生代 Survivor 空間不足

JVM 調優 —— 新生代 Survivor 空間不足

會有 使用 bytes 情況 導致 class n) 可能 多少

零. 新生代調優規律 增大新生代空間。 Minor GC 頻率降低, Minor GC 時間上升。 降低新生代空間, Minor GC 頻率上升, Minor GC 時間下降

一. 新生代典型問題

先看一段 GC 日誌:新生代使用 ParNew。 老年代使用 CMS

{Heap before GC invocations=0 (full 0):
 par new generation   total 943744K, used 838912K [0x0000000757000000, 0x0000000797000000, 0x0000000797000000)
  eden space 838912K, 100% used [0x0000000757000000, 0x000000078a340000, 0x000000078a340000)
  from space 104832K,   0% used [0x000000078a340000, 0x000000078a340000, 0x00000007909a0000)
  to   space 104832K,   0% used [0x00000007909a0000, 0x00000007909a0000, 0x0000000797000000)
 concurrent mark-sweep generation total 1560576K, used 0K [0x0000000797000000, 0x00000007f6400000, 0x00000007f6400000)
 concurrent-mark-sweep perm gen total 159744K, used 38069K [0x00000007f6400000, 0x0000000800000000, 0x0000000800000000)
2016-01-19T14:15:34.532+0800: 13.812: [GC2016-02-19T14:15:34.532+0800: 13.812: [ParNew
Desired survivor size 53673984 bytes, new threshold 1 (max 6)
- age   1:   55521392 bytes,   55521392 total
: 838912K->54474K(943744K), 0.0914620 secs] 838912K->54474K(2504320K), 0.0916240 secs] [Times: user=0.67 sys=0.06, real=0.09 secs]
Heap after GC invocations=1 (full 0):
 par new generation   total 943744K, used 54474K [0x0000000757000000, 0x0000000797000000, 0x0000000797000000)
  eden space 838912K,   0% used [0x0000000757000000, 0x0000000757000000, 0x000000078a340000)
  from space 104832K,  51% used [0x00000007909a0000, 0x0000000793ed2ae0, 0x0000000797000000)
  to   space 104832K,   0% used [0x000000078a340000, 0x000000078a340000, 0x00000007909a0000)
 concurrent mark-sweep generation total 1560576K, used 0K [0x0000000797000000, 0x00000007f6400000, 0x00000007f6400000)
 concurrent-mark-sweep perm gen total 159744K, used 38069K [0x00000007f6400000, 0x0000000800000000, 0x0000000800000000)
}
{Heap before GC invocations=1 (full 0):
 par new generation   total 943744K, used 893386K [0x0000000757000000, 0x0000000797000000, 0x0000000797000000)
  eden space 838912K, 100% used [0x0000000757000000, 0x000000078a340000, 0x000000078a340000)
  from space 104832K,  51% used [0x00000007909a0000, 0x0000000793ed2ae0, 0x0000000797000000)
  to   space 104832K,   0% used [0x000000078a340000, 0x000000078a340000, 0x00000007909a0000)
 concurrent mark-sweep generation total 1560576K, used 0K [0x0000000797000000, 0x00000007f6400000, 0x00000007f6400000)
 concurrent-mark-sweep perm gen total 159744K, used 53249K [0x00000007f6400000, 0x0000000800000000, 0x0000000800000000)
2016-01-19T14:15:41.943+0800: 21.222: [GC2016-02-19T14:15:41.943+0800: 21.223: [ParNew
Desired survivor size 53673984 bytes, new threshold 1 (max 6)
- age   1:  107256200 bytes,  107256200 total
: 893386K->104832K(943744K), 1.2389070 secs] 893386K->210614K(2504320K), 1.2391870 secs] [Times: user=2.89 sys=0.35, real=1.24 secs]
Heap after GC invocations=2 (full 0):
 par new generation   total 943744K, used 104832K [0x0000000757000000, 0x0000000797000000, 0x0000000797000000)
  eden space 838912K,   0% used [0x0000000757000000, 0x0000000757000000, 0x000000078a340000)
  from space 104832K, 100% used [0x000000078a340000, 0x00000007909a0000, 0x00000007909a0000)
  to   space 104832K,   0% used [0x00000007909a0000, 0x00000007909a0000, 0x0000000797000000)
  concurrent mark-sweep generation total 1560576K, used 105782K [0x0000000797000000, 0x00000007f6400000, 0x00000007f6400000)
 concurrent-mark-sweep perm gen total 159744K, used 53249K [0x00000007f6400000, 0x0000000800000000, 0x0000000800000000)
}



能夠明顯看出上述 GC 日誌包括兩次 Minor GC。 註意到第二次 Minor GC 的情況, 日誌打出 "Desired survivor size 53673984 bytes"。 可是卻存活了 "- age 1: 107256200 bytes, 107256200 total" 這麽多。 能夠看出明顯的新生代的 Survivor 空間不足。正由於 Survivor 空間不足, 那麽從 Eden 存活下來的和原來在 Survivor 空間中不夠老的對象占滿 Survivor 後, 就會提升到老年代, 能夠看到這一輪 Minor GC 後老年代由原來的 0K 占用變成了 105782K 占用, 這屬於一個典型的 JVM 內存問題。 稱為 "premature promotion"(過早提升)。
"premature promotion” 在短期看來不會有問題, 可是常常性的 "premature promotion”, 最總會導致大量短期對象被提升到老年代, 終於導致老年代空間不足, 引發還有一個 JVM 內存問題 “promotion failure”(提升失敗: 即老年代空間不足以容乃 Minor GC 中提升上來的對象)。 “promotion failure” 發生就會讓 JVM 進行一次 CMS 垃圾收集進而騰出空間接受新生代提升上來的對象。 CMS 垃圾收集時間比 Minor GC 長, 導致吞吐量下降、 時延上升, 將對用戶體驗造成影響。

二. 新生代調優建議
對於上述的新生代問題, 假設server內存足夠用, 建議是直接增大新生代空間(如 -Xmn)。

假設內存不夠用。 則添加 Survivor 空間, 降低 Eden 空間, 可是註意降低 Eden 空間會添加 Minor GC 頻率, 要考慮到應用對延遲和吞吐量的指標終於是否符合。


要增大多少 Survivor 空間? 須要觀察多次 Minor GC 過程。 看 Minor GC 後存活下來的對象大小。 終於確定 Survivor 的合適大小。 整個調優過程可能須要幾次調整。 才幹找到比較合適的值。調整幾次後, 假設內存還是不夠用, 就要須要考慮增大server內存, 或者把負載分擔到很多其它的 JVM 實例上。
Survivor 空間計算公式: survivor 空間大小 = -Xmn[value] / (-XX:SurvivorRatio=<ratio> + 2)


JVM 調優 —— 新生代 Survivor 空間不足