jvm堆大小的设置

问题引入:

-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3,,其最小内存值和Survivor区总大小分别是(10240m    2048m);

解析:

-Xmx:最大堆大小

-Xms:初始堆大小

-Xmn:年轻代大小

-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值

年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。

-Xms初始堆大小即最小内存值为10240m。

 

下面来解释下几个重要参数的含义:

-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中的永久代(方法区)的大小。可以看到:永久代不属于堆内存,堆内存只包含新生代和老年代。

 

可以发现:堆内存、新生代内存、老年代内存、永久代内存,都有一个初始内存,还有一个最大内存。下面以老年代的初始内存和最大内存为例,看下内存变化的效果,其他的应该类似。测试代码如下:

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

                }

        }

}

显然这个程序存在内存泄露,最终会占满整个堆内存,抛出OOM。为了看清楚这个演变的过程,我们在while循环中添加一个断点,设置breakpoint properties中的"hit count"为100000,以debug模式运行上面的程序,然后使用jmap观察内存占用情况。

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。

 

六、有关年轻代的JVM参数

1)-XX:NewSize和-XX:MaxNewSize(jdk1.3or1.4)

用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。

2)-Xmn(jdk1.4or lator)

用于设置年轻代大小。例如:-Xmn10m,设置新生代大小为10m。此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是(eden+1 survivor space)不同的。

新生代大小:Eden区加上一个survivor区

3)-XX:SurvivorRatio

用于设置Eden和其中一个Survivor的比值,默认比例为8(Eden):1(一个survivor),这个值也比较重要。

例如:-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6。

例子:-XX:SurvivorRatio=8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。

 

Scavenge GC 或minor GC

一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC

对整个堆进行整理,包括YoungTenuredPermFull GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC

· 年老代(Tenured)被写满

· 持久代(Perm)被写满

· System.gc()被显示调用

·上一次GC之后Heap的各域分配策略动态变化

 

Minor GC ,Full GC 触发条件

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:

(1)调用System.gc时,系统建议执行Full GC,但是不必然执行

(2)老年代空间不足

(3)方法去空间不足

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

 

如何设置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

 

 

 

 

参考链接:

http://www.importnew.com/15820.html

https://blog.csdn.net/yhyr_ycy/article/details/52566105

https://blog.csdn.net/blueheart20/article/details/52092535#chatqa

https://www.cnblogs.com/lytwajue/p/7120031.html

https://www.cnblogs.com/shoshana-kong/p/9071004.html

https://blog.csdn.net/losetowin/article/details/78569001

https://blog.csdn.net/blueheart20/article/details/52092535

https://blog.csdn.net/aitangyong/article/details/39344443

https://www.cnblogs.com/shoshana-kong/p/9071004.html

 

 

  • 11
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值