• 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 list = new ArrayList();

    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

    对整个堆进行整理,包括Young、Tenured和Perm。Full 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 将当前的存活对象dump到文件,此时会触发FullGC

    jmap -histo:live 打印每个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

    )

  • 相关阅读:
    基于ubuntu20.04安装ros系统搭配使用工业相机
    Socket套接字
    Matlab 机器人工具箱 动力学
    Spring注解开发
    kubernetes Service详解
    【POJ No. 3468】 简单的整数问题 A Simple Problem with Integers
    python实现目标检测voc格式标签数据增强
    【matlab学习】现代控制
    产品经理专业知识50篇(四)-从问题到能力提升:AMDGF模型工具
    共享盘的文件删除后能找回吗
  • 原文地址:https://blog.csdn.net/m0_67393295/article/details/126744394