• 图解Java对象在Heap内存中的分配过程


    为新对象分配内存是一个非常严谨和复杂的任务。JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配空间算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收是否会在内存空间中产生内存碎片。 

    Java对象的分配和回收过程:

    • 创建的对象首先被放在Eden区。(此区有大小限制)
    • Eden区空间填满时,程序又需要创建对象,JVM的垃圾收集器将会对Eden区进行YoungGC(Minor GC),将Eden区中的不再被其它对象所引用的对象进行销毁。后边再加载进来的新对象依然首选放到Eden区。
    • 然后将Eden区中剩余对象移动到Survivor0区
    • 如果再次触发YoungGC,此时上次幸存下来的对象将被S0区,如果没有回收,就会放到S1区。

    • 如果再次经历垃圾回收,此时会重新放回S0区,接着再去S1区。

    • 何时将对象转移到老年代区?可以设置对象的存活周期阈值。默认是15次。

            -XX:MaxTenuringThreshold = 进行设置

    • 在老年代区,当老年代区内存不足时,触发OldGC(Major GC),进行老年代区的内存清理。
    • 如果老年代区执行了MajorGC后,发现依然无法进行对象的保存,就会OOM异常

            java.lang.OutOfMemoryErro: Java heap space

    小结:

    1-针对幸存者S0(From/To)、S1(From/To)区,复制之后有交换,谁空谁是To区

    2-关于垃圾回收:对象回收一般频繁会在新生代区收集,很少在老年代区收集,几乎不在永久代/元空间收集。


    对象在Heap内存中的分配和回收流程图

    前边关于对象在内存中分配过程,可以用下边流程图表示,过程已经很详细了,不再赘述。


    内存分配策略(对象晋升Promotion规则)

    如果对象的Eden区出生,并经过一次Minor GC后依然存活,并且能够被Survivor区容纳的话,将被移动到Survivor区,并且对象的存活年龄设为1。

    对象在Survivor区中每熬过一次MinorGC,存活年龄值就增加1,当它的年龄增加到一定的程度(默认为15岁,每个GC各不相同),就会晋升到老年代中。

    对象晋升的阈值可以通过选项 -XX:MaxTenuringThreshold来设置。

    针对不同年龄段的对象分配原则如下:

    • 新生对象有限被分配到Eden区
    • 大对象直接进入到老年代

            避免程序中出现较多的大对象。

    • 长期存活的对象分配到老年代
    • 动态年龄判断

    如果survivor区间中相同年龄的所有对象的大小总和大于survivor区间的一半,年龄大于或等于该年龄的对象可以直接进入到老年代区,无需等到MasTenuringThreshold中设置的晋升年龄阈值。

    举个栗子:

    如对象年龄5的占30%,年龄6的占36%,年龄7的占34%,加入某个年龄段(如例子中的年龄6)后,总占用超过Survivor空间*TargetSurvivorRatio的时候,从该年龄段开始及大于的年龄对象就要进入老年代(即例子中的年龄6对象,就是年龄6和年龄7晋升到老年代),这时候无需等到MaxTenuringThreshold中要求的15。

    • 空间分配担保原则

    空间分配担保目的:在年轻代进行Minor GC前,老年代本身应该还有容纳新生代所有对象的剩余空间。如果不够,则进行Full GC。

    配置参数:-XX:HandlePromotionFailure   是否设置空间分配担保

    大对象直接进入老年代栗子:

    1. /**
    2. * 测试大对象直接进入老年代
    3. *
    4. * -Xmx40m -Xms40m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
    5. */
    6. public class YoungOldAreaDemo {
    7. public static void main(String[] args) {
    8. // 创建一个20m大小的对象
    9. byte[] data = new byte[1024 * 1024 * 20];
    10. }
    11. }

    运行前先做一下VM参数配置配置:

     控制台输出结果:

     因为设置总的堆内存大小只有40m,新生代空间12m,老年代空间27m,新建的20m大对象将直接晋升到老年代区间。

  • 相关阅读:
    golang validator 包的使用指北
    手敲,Ascend算子开发入门笔记分享
    tp6快速安装使用MongoDB实现增删改查
    Kotlin高仿微信-第3篇-主页
    maven3.6.3版本下载安装
    C++ 11 & 14 中的Lambda表达式 补充
    drools动态增加、修改、删除规则
    配电房监测系统:智能化电力管理的关键
    记录 Maven 版本覆盖 Bug 的解决过程
    常用音频接口:TDM,PDM,I2S,PCM
  • 原文地址:https://blog.csdn.net/qq_27706119/article/details/126842725