JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

 

JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

2015年03月27日 15:38:48

阅读数:2091

如果想观察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里面的推荐公式来进行设置。

JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

 

具体来讲:

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情况。如下图:

JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

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>)

JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

上图中的concurrent mark-sweep generation即为老年代的内存描述。

 

 

老年代的内存占用为100M左右。 按照整个堆大小是老年代(FullGC)之后的3-4倍计算的话,设置各代的内存情况如下:

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

 

调整之后的,heap情况

JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

GC情况如下:

JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

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堆内存相关的启动参数:年轻代、老年代和永久代的内存分配版权所有:《攀爬蜗牛》 => 《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