1. 程式人生 > >JVM常用調優案例

JVM常用調優案例

在本文中會介紹一些常用的JVM調優思路以及調優方法,這些方法是為了解決某個具體問題,提高某個區域性效能而特別設定的。 使用它們可能會對其他效能指標產生不良的影響,應該在實際應用中,根據具體情況進行折中和權衡。總結自《Java程式效能優化》

1.將新物件預留在新生代

由於Full GC的成本遠高於Minor GC,因此儘可能將物件分配在新生代是一項明智的做法。雖然在大部分情況下,JVM會嘗試在eden區分配物件,但是由於空間緊張等問題,很可能不得不將部分年輕物件提前向老年代壓縮。

在JVM引數調優中,可以為應用程式分配一個合理的新生代空間,以避免新物件直接進入老年代的情況。因為新生代垃圾回收的速度高於老年代回收。因此,將年輕物件預留在新生代有利於提升整體的GC效率。

2.大物件進入老年代

雖然在大部分情況下,將物件分配在新生代是合理的。但是,對於大物件,這種做法是值得商榷的。大物件出現在新生代很可能擾亂新生代GC,並破壞新生代原有的物件結構。

因為嘗試在新生代分配大物件,很可能導致空間不足,為了有足夠的空間容納大物件,JVM不得不將新生代中的年輕物件挪到老年代。因為大物件佔用空間多,所以可能需要移動大量的小的年輕物件進入老年代。因為新生代的物件往往存活時間比較短,所以會引起多次的Full GC造成程式卡頓。

基於以上的原因,可以直接將大物件分配到老年代,以保持新生代物件結構的完整性,提高GC的效率。

可以使用引數-XX:PretenureSizeThreshold設定大物件直接進入老年代的閾值。當物件的大小超過這個值,將直接在老年代分配。

3.設定物件進入老年代的年齡

在堆中,每個物件都有自己的年齡,一般情況下,年輕物件存放在新生區,年老物件存放在老年區。為了做到這一點,JVM會為每一個物件都維護一個年齡

如果物件在eden區,經過一次GC之後還存活,則被移動到survivor區中,物件年齡加1 。之後物件每經過一次GC之後依然存活,則年齡再加1 。當物件年齡到達閾值,就移入老年代,成為老年物件。

這個閾值的最大值可以通過引數-XX:MaxTenuringThreshold來設定,它的預設值為15.但這並不意味著新物件一定要達到這個年齡才能進入老年代。物件實際進入老年代的年齡是虛擬機器在執行時根據記憶體使用情況動態計算的,這個引數指定的是閾值的最大值。即實際進入老年代的年齡等於動態計算所得的年齡與引數指定的中較小的那一個。

4.穩定與震盪的堆大小

一般來說,穩定的堆大小對垃圾回收是有利的。獲得一個穩定的堆大小的方法是使-Xms和-xmx的大小一致,即最大堆和最小堆都一樣。

如果這樣設定,系統在執行時,堆的大小是恆定的,穩定的堆大小可以減少GC的次數,因此很多 服務端應用都會將最大堆和最小堆設定為相同的數值。

但是一個不穩定的堆大小也並非一無是處。穩定的堆大小雖然減少了GC的次數,但是同時也增加了GC的時間。讓堆大小在一個區間中震盪,在系統不需要使用大記憶體時,壓縮堆空間,使GC應對一個較小的堆可以加快單次GC的速度。

基於這樣的考慮,JVM還提供了兩個引數用於壓縮和拓展堆空間:

  1. -XX:MinHeapFreeRatio : 設定堆空間的最小空閒比例,預設是40.當堆空間的空閒記憶體小於這個數值時,JVM會拓展堆空間。
  2. -XX:MinHeapFreeRatio : 設定堆空間的最大空閒比例,預設是70.當堆空間的空閒記憶體大於這個數值時,JVM會拓展空間。

當-XMS和-Xmx相等時,這兩個引數是無效的。