天天看點

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

一、Java虛拟機記憶體模型

·  JVM記憶體區域模型

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

    如上圖所示,JVM記憶體區域分為程式計數器、虛拟機棧、本地方法棧、堆、方法區這5個區域,來保證程式正常運作。

·  程式計數器

    程式計數器在JVM記憶體中是一塊很小的空間,他是目前線程所需要執行的位元組碼的行号訓示器。位元組碼可通過改變程式計數器中的值來選擇跳轉、分支、循環、異常處理等指令。是以每個線程都有自己的一個程式計數器,并且多個線程之間的程式計數器互相不幹擾,是線程私有的并且生命周期與線程相同。

    如果目前線程執行的是java方法那麼程式計數器記錄的是目前正在執行的java位元組碼位址。如果目前線程執行的是native方法,那麼程式計數器就不會記錄,并且目前程式計數器為空值。

·  虛拟機棧

    Java虛拟機棧已在之前釋出的文章中提到過,是以不做具體闡述。他是線程私有的記憶體空間,并且生命周期也與線程相同。虛拟機棧的核心是棧幀,棧幀中存放着目前線程方法的局部變量表、操作數棧、動态連結、傳回位址等資訊。一個線程中的方法被調用就會建立一個棧幀(入棧),方法執行的結束意味着這個棧幀也會被銷毀(出棧)。

    棧幀結構如下圖:

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

·  本地方法棧

    本地方法棧負責管理的是虛拟機本地方法(native方法)的調用。他的功能與虛拟機棧是類似的(虛拟機棧負責管理的是java方法的調用)。

·  堆

    堆的空間在虛拟機中擁有配置設定最大的空間。因為所有線程中對象執行個體的建立都會在堆空間中并且堆的空間是所有線程共享的,是以在堆中會發生很頻繁的gc操作。

    堆空間中分為新生代和老年代兩塊區域,新生代中主要存放的是剛剛産生和一些被gc回收後仍幸存的對象執行個體,老生代中主要存放的是一直沒被gc回收的對象執行個體。是以當對象剛被new出來時會被放入新生代中,經過多次gc回收該對象仍未被回收,則會被移入到老年代中。

    堆空間中的新生代中又被分為eden區、幸存區(有些jvm若選擇了标記-複制回收算法,則會存在2個幸存區from space,to space)。

    堆空間結構如下圖:

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

        設定JVM參數模拟gc回收過程:-XX:+PrintGCDetails -Xmx6M -Xms6M -Xmn3M。表示最大堆記憶體為6M,新生代記憶體空間為3M。

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

    運作上述圖中代碼,gc結果如下圖:

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

    由于修改jvm參數仍調試不出minor gc的過程,是以配圖隻有full gc的過程。可以發現在經曆full gc後,新生代空間被清空。b1、b2對象為被清空,進入老生代。

·  方法區

    方法區記憶體空間也是被線程所共享的,他主要存儲被虛拟機加載的類資訊、常量池、靜态變量。上圖中方法區的空間為metaSpace。方法區中存儲的資料大部分來自于java的class檔案,也是java應用程式運作的重要資料。

    當方法區中空間達到飽和狀态時,也會喚起full gc來進行回收。如果不能及時回收也會像堆空間一樣抛出OutOfMemoryError異常。

    使用之前文章中String中提到過的intern()方法來将字元串直接加入方法區中的常量池來模拟方法區中的gc。

    設定JVM參數-XX:+PrintGCDetails -XX:PermSize=2M -XX:MaxPermSize=4M。設定方法區初始值為2M,最大的方法區空間為4M。

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

    運作上述代碼,gc結果如下:

[Perm: 4096K->528K(4096K)] 4096K->536K(4096K), 0.0139378 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 

[Perm: 4096K->512K(71680K)] 4096K->520K(4096K), 0.0246657 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 

[Perm: 4096K->848K(71680K)] 4096K->864K(159232K), 0.0830849 secs] [Times: user=0.08 sys=0.01, real=0.09 secs] 

二、VM Options參數設定

· 設定最大堆記憶體-Xmx

    -Xmx可以用來設定堆的最大空間。-Xmx參數設定的不同,将直接決定程式何時會抛出OutOfMemory異常。

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

    設定-Xmx1M後運作上圖程式,運作結果:

    第1次配置設定空間

    第2次配置設定空間

    第3次配置設定空間

    第4次配置設定空間

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

發現,第5次配置設定空間時堆記憶體空間就超過了設定的最大值1M,導緻抛出記憶體溢出異常。程式中将bytes放入list集合中目的是為了給bytest增加強引用,保證他不被垃圾回收掉。

    設定-Xmx2M後運作上圖程式,運作結果:

    第12次配置設定空間

    第12次配置設定空間

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

·  設定最小堆空間-Xms

    -Xms可以用來設定最小堆空間,程式在運作時,被配置設定空間為目前設定的最小堆記憶體,是以jvm會維護該最小堆記憶體,通過gc來保證程式處于最小堆記憶體中。當最小堆記憶體不能滿足于目前程式所需要的空間時,才會使用比最小堆記憶體更大的空間,但是堆空間仍不能超過-Xmx設定的最大空間,否則也會抛出OutOfMemoryError。

jvm記憶體參數配置_記憶體區域 模型與JVM調優參數詳解

    設定JVM參數-Xms4M -Xms10M,運作結果如下:

[GC  7900K->6876K(9728K), 0.0006741 secs]

[GC  7900K->6876K(9728K), 0.0008844 secs]

[GC  7900K->7900K(9728K), 0.0026272 secs]

[Full GC  7900K->1756K(9728K), 0.0058599 secs]

[GC  7900K->6876K(9728K), 0.0007725 secs]

[GC  7900K->6876K(9728K), 0.0004481 secs]

[GC  7900K->7900K(9728K), 0.0005057 secs]

[Full GC 7900K->1756K(9728K), 0.0045034 secs]

    為了保證程式在-Xms設定的範圍内運作,會頻繁的增加minor gc和full gc的次數,對性能産生一定影響。

    提高為-Xms10M,運作結果:

[GC  7900K->4276K(9728K), 0.0001241 secs]

[GC  7900K->5816K(9728K), 0.0005144 secs]

[GC  7900K->6100K(9728K), 0.0009122 secs]

·  設定新生代-Xmn

    參數-Xmn用于設定堆中新生代的大小。設定一個較大的新生代會減少老年代的大小,如果新生代的大小設定太小,會導緻新生代空間不足造成頻繁進行minor gc。

    當設定-Xmn時,表示-XX:NewSize(新生代初始大小)和-XX:MaxNewSize(新生代的最大值)設定了相同的大小。

    在新生代的設定中,不建議設定不同的-XX:NewSize和-XX:MaxNewSize,會導緻頻繁minor gc增加不必要的系統開銷。隻設定-Xmn即可。

·  設定堆中新生代的比例配置設定-XX:SurvivorRatio

    參數-XX:SurvivorRatio用來設定堆中新生代的eden區和幸存區的比例。由于大部分gc算法都采用了分代回收算法,是以新生代一般會存在eden、from s1,to s2三塊空間。

    示例:-XX:SurvivorRatio=8 -Xmn10M 計算新生代三塊空間占用大小。

    -Xmn上述已經提到表示為新生代大小,是以這裡新生代大小為10M。-XX:SurvivorRatio=8說明eden:from s1:to s2 = 8:1:1。是以eden區的大小為8M,from s1的大小為1M,to s2的大小為1M。

· 設定堆的比例配置設定-XX:NewRatio

    參數-XX:NewRatio用來設定堆中老年代和新生代的比例。

    示例:-XX:NewRatio=2 -Xmx30M -Xms 30M,計算新生代和老年代空間大小。

    -Xmx30M和-Xms30M說明該堆空間為30M,-XX:NewRatio=2老年代:新生代為2:1,是以老年代大小為20M,新生代大小為10M。

·  設定方法區-XX:PermSize -XX:MaxPermSize

    -XX:permSize表示方法區的初始大小,-XX:MaxPermSize表示方法區的最大值,若方法區空間超過了最大值,也會抛出OutOfMemoryError異常。

    一般來說MaxPermSize設定為64MB或128MB已經可以滿足大部分程式正常工作。

三、堆記憶體gc總結

    堆被分為新生代和老生代,新生代被分為eden區和幸存區。當對象剛被new出來時,會被配置設定到新生代中的eden區,eden區的gc很頻繁。随後經過gc多次回收,如果還存在,則會被移到幸存區,幸存區的gc相比eden區少了很多。經過gc多次回收後,若還存在,則會被移到老生代,老生代中不會很頻繁的發生gc,如果老生代中的對象被回收那麼很有可能造成系統的卡頓甚至崩潰。