JVM堆內存相關的啟動參數:年輕代、老年代和永久代的內存分配


轉自:https://blog.csdn.net/xuheng8600/article/details/81478426

如果想觀察JVM進程占用的堆內存,可以通過命令工具jmap或者可視化工具jvisualvm.exe。JVM這些啟動參數都擁有默認值,如果想了解JVM的內存分配策略,最好手動設置這些啟動參數。再通過JDK提供的工具的統計結果,進行對比,就比較容易理解這些內存分配的理論知識。運行環境是win7 32位操作系統,JDK1.7.0_60版本。

測試代碼和JVM啟動參數如下:

?

1

2

3

4

5

6

7

8

9

10

11

public class  Test

{

    public static  void  main(String[] args)

    {

        int a = 0;

        while (true)

        {

            a++;

        }

    }

}

?

1

2

-Xms=200M  -Xmx200M -XX:NewSize=100M -Xmn100M -XX:SurvivorRatio=8

-XX:OldSize=60M -XX:PermSize=50M -XX:MaxPermSize=50M

 

運行上述代碼,通過jps命令獲取到進程pid,然后通過jmap -heap pid就可以查看內存分配和使用情況。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

>jmap -heap 8912

Attaching to process ID 8912, please wait...

Debugger attached successfully.

Client compiler detected.

JVM version is 24.60-b09

 

using thread-local object allocation.

Mark Sweep Compact GC

 

Heap Configuration:

   MinHeapFreeRatio = 40

   MaxHeapFreeRatio = 70

   MaxHeapSize      = 209715200 (200.0MB)

   NewSize          = 104857600 (100.0MB)

   MaxNewSize       = 104857600 (100.0MB)

   OldSize          = 62914560 (60.0MB)

   NewRatio         = 3

   SurvivorRatio    = 8

   PermSize         = 52428800 (50.0MB)

   MaxPermSize      = 52428800 (50.0MB)

這里顯示的堆配置參數,都可以通過JVM啟動參數來設置。下面來解釋下幾個重要參數的含義:


-Xms 和 -Xmx (-XX:InitialHeapSize 和 -XX:MaxHeapSize):指定JVM初始占用的堆內存和最大堆內存。JVM也是一個軟件,也必須要獲取本機的物理內

存,然后JVM會負責管理向操作系統申請到的內存資源。JVM啟動的時候會向操作系統申請 -Xms 設置的內存,JVM啟動后運行一段時間,如果發現內存空間

不足,會再次向操作系統申請內存。JVM能夠獲取到的最大堆內存是-Xmx設置的值。

 

-XX:NewSize 和 -Xmn(-XX:MaxNewSize):指定JVM啟動時分配的新生代內存和新生代最大內存。

 

-XX:SurvivorRatio:設置新生代中1個Eden區與1個Survivor區的大小比值。在hotspot虛擬機中,新生代 = 1個Eden + 2個Survivor。如果新生代內存是

10M,SurvivorRatio=8,那么Eden區占8M,2個Survivor區各占1M。

 

-XX:NewRatio:指定老年代/新生代的堆內存比例。在hotspot虛擬機中,堆內存 = 新生代 + 老年代。如果-XX:NewRatio=4表示年輕代與年老代所占比值為1:4,年輕代占整個堆內存的1/5。在設置了-XX:MaxNewSize的情況下,-XX:NewRatio的值會被忽略,老年代的內存=堆內存 - 新生代內存。老年代的最大內存 = 堆內存 - 新生代 最大內存。

 

-XX:OldSize:設置JVM啟動分配的老年代內存大小,類似於新生代內存的初始大小-XX:NewSize。

 

-XX:PermSize 和 -XX:MaxPermSize:指定JVM中的永久代(方法區)的大小。可以看到:永久代不屬於堆內存,堆內存只包含新生代和老年代。

 

可以發現:堆內存、新生代內存、老年代內存、永久代內存,都有一個初始內存,還有一個最大內存。下面以老年代的初始內存和最大內存為例,看下內存變化的效果,其他的應該類似。測試代碼如下:

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public class  TurnedTest

{

    private static  List<string> list = new ArrayList<string>();

 

    public static  void  main(String[] args)

    {

        int a = 0;

        while (true)

        {

            a++;

 

            list.add("demo");

        }

 

    }

}</string></string>

顯然這個程序存在內存泄露,最終會占滿整個堆內存,拋出OOM。為了看清楚這個演變的過程,我們在while循環中添加一個斷點,設置breakpoint properties中的"hit count"為100000,以debug模式運行上面的程序,然后使用jmap觀察內存占用情況。

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

tenured generation:

capacity = 62914560 (60.0MB)

used     = 0 (0.0MB)

free     = 62914560 (60.0MB)

0.0% used

 

 

tenured generation:

capacity = 62914560 (60.0MB)

used     = 16409080 (15.648918151855469MB)

free     = 46505480 (44.35108184814453MB)

26.08153025309245% used

 

tenured generation:

capacity = 62914560 (60.0MB)

used     = 53329496 (50.858970642089844MB)

free     = 9585064 (9.141029357910156MB)

84.76495107014973% used

 

tenured generation:

capacity = 104857600 (100.0MB)

used     = 84217880 (80.3164291381836MB)

free     = 20639720 (19.683570861816406MB)

80.3164291381836% used

可以發現老年代內存從最開始的60M,擴大到最大值100M。

https://blog.csdn.net/flamezyg/article/details/44677733

 

JVM內存設置多大合適?Xmx和Xmn如何設置?

losetowin 發布於:2017-11-3 11:19 分類:Java  有 4305 人瀏覽,獲得評論 0 條  

本文地址:http://www.dutycode.com/jvm_xmx_xmn_xms_shezhi.html
除非注明,文章均為 www.dutycode.com 原創,歡迎轉載!轉載請注明本文地址,謝謝。

問題:

新上線一個java服務,或者是RPC或者是WEB站點, 內存的設置該怎么設置呢?設置成多大比較合適,既不浪費內存,又不影響性能呢?

 

 

分析:

依據的原則是根據Java Performance里面的推薦公式來進行設置。

WX20171103-112002.png

 

 

具體來講:

Java整個堆大小設置,Xmx 和 Xms設置為老年代存活對象的3-4倍,即FullGC之后的老年代內存占用的3-4倍

永久代 PermSize和MaxPermSize設置為老年代存活對象的1.2-1.5倍。

年輕代Xmn的設置為老年代存活對象的1-1.5倍。

老年代的內存大小設置為老年代存活對象的2-3倍。

 

BTW:

     1、Sun官方建議年輕代的大小為整個堆的3/8左右, 所以按照上述設置的方式,基本符合Sun的建議。 

     2、堆大小=年輕代大小+年老代大小, 即xmx=xmn+老年代大小 。 Permsize不影響堆大小。

     3、為什么要按照上面的來進行設置呢? 沒有具體的說明,但應該是根據多種調優之后得出的一個結論。

 

如何確認老年代存活對象大小?

方式1(推薦/比較穩妥):

     JVM參數中添加GC日志,GC日志中會記錄每次FullGC之后各代的內存大小,觀察老年代GC之后的空間大小。可觀察一段時間內(比如2天)的FullGC之后的內存情況,根據多次的FullGC之后的老年代的空間大小數據來預估FullGC之后老年代的存活對象大小(可根據多次FullGC之后的內存大小取平均值)

 

方式2:(強制觸發FullGC, 會影響線上服務,慎用)

     方式1的方式比較可行,但需要更改JVM參數,並分析日志。同時,在使用CMS回收器的時候,有可能不能觸發FullGC(只發生CMS GC),所以日志中並沒有記錄FullGC的日志。在分析的時候就比較難處理。

     BTW:使用jstat -gcutil工具來看FullGC的時候, CMS GC是會造成2次的FullGC次數增加。 具體可參見之前寫的一篇關於jstat使用的文章

     所以,有時候需要強制觸發一次FullGC,來觀察FullGC之后的老年代存活對象大小。

     注:強制觸發FullGC,會造成線上服務停頓(STW),要謹慎,建議的操作方式為,在強制FullGC前先把服務節點摘除,FullGC之后再將服務掛回可用節點,對外提供服務

     在不同時間段觸發FullGC,根據多次FullGC之后的老年代內存情況來預估FullGC之后的老年代存活對象大小

 

如何觸發FullGC ?

               使用jmap工具可觸發FullGC 

               jmap -dump:live,format=b,file=heap.bin <pid> 將當前的存活對象dump到文件,此時會觸發FullGC

               jmap -histo:live <pid> 打印每個class的實例數目,內存占用,類全名信息.live子參數加上后,只統計活的對象數量. 此時會觸發FullGC

 

 

具體操作實例:

以我司的一個RPC服務為例。

 

BTW:剛上線的新服務,不知道該設置多大的內存的時候,可以先多設置一點內存,然后根據GC之后的情況來進行分析。

初始JVM內存參數設置為: Xmx=2G Xms=2G xmn=1G 

 

使用jstat 查看當前的GC情況。如下圖:

WX20171103-112025.png

 

YGC平均耗時: 173.825s/15799=11ms

FGC平均耗時:0.817s/41=19.9ms

平均大約10-20s會產生一次YGC

 

看起來似乎不錯,YGC觸發的頻率不高,FGC的耗時也不高,但這樣的內存設置是不是有些浪費呢?

為了快速看數據,我們使用了方式2,產生了幾次FullGC,FullGC之后,使用的jmap -heap 來看的當前的堆內存情況(也可以根據GC日志來看)

heap情況如下圖:(命令 : jmap -heap <pid>)

WX20171103-112039.png

 

上圖中的concurrent mark-sweep generation即為老年代的內存描述。

 

 

老年代的內存占用為100M左右。 按照整個堆大小是老年代(FullGC)之后的3-4倍計算的話,設置各代的內存情況如下:

     Xmx=512m  Xms=512m  Xmn=128m PermSize=128m  老年代的大小為 (512-128=384m)為老年代存活對象大小的3倍左右

 

調整之后的,heap情況

WX20171103-112102.png

 

GC情況如下:

WX20171103-112111.png

 

YGC 差不多在10s左右觸發一次。每次YGC平均耗時大約9.41ms。可接受。

FGC平均耗時:0.016s/2=8ms

整體的GC耗時減少。但GC頻率比之前的2G時的要多了一些。

 

注: 看上述GC的時候,發現YGC的次數突然會增多很多個,比如 從1359次到了1364次。具體原因是?

 

總結:

在內存相對緊張的情況下,可以按照上述的方式來進行內存的調優, 找到一個在GC頻率和GC耗時上都可接受的一個內存設置,可以用較小的內存滿足當前的服務需要

 

但當內存相對寬裕的時候,可以相對給服務多增加一點內存,可以減少GC的頻率,GC的耗時相應會增加一些。 一般要求低延時的可以考慮多設置一點內存, 對延時要求不高的,可以按照上述方式設置較小內存。 

 

 

補充:

永久代(方法區)並不在堆內,所以之前有看過一篇文章中描述的 整個堆大小=年輕代+年老代+永久代的描述是不正確的。 

 

 

 

 

問題:

1、GC頻率和GC時間多少合適?

2、YGC何時觸發,FGC何時觸發?

3、內存設置越大,GC的耗時是否就會越長?為什么? 

版權所有:《攀爬蝸牛》 => 《JVM內存設置多大合適?Xmx和Xmn如何設置?
本文地址:https://www.dutycode.com/jvm_xmx_xmn_xms_shezhi.html
除非注明,文章均為 《攀爬蝸牛》 原創,歡迎轉載!轉載請注明本文地址,謝謝。

上一篇:MacOsx實用軟件推薦(Java程序員版) 
下一篇:Java命令-jstat使用

http://www.dutycode.com/jvm_xmx_xmn_xms_shezhi.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM