• jvm内存分配与回收策略


    自动内存管理

    解决两个问题

    自动给对象分配内存

    对象一般堆上分配(而实际上也有可能经过即时编译后被拆散为标量类型并间接地在栈上分配)

    新生对象通常会分配在新生代,少数情况下(例如对象大小超过一定阈值)也可能会直接分配在老年代

    实际对象分配的规则并不是固定的,具体取决于虚拟机使用的是哪一种垃圾收集器以及虚拟机中与内存相关参数的设定

    自动回收分配给对象的内存(垃圾收集器做的事)

    对象优先在Eden分配

    大多数情况下,对象在新生代Eden区中分配当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

    -XX:+PrintGCDetails:收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。

    image-20230927232825248

    参数含义:

    -Xms20M(初始) -Xmx20M(最大):堆内存固定20m

    -Xmn10M:10MB分配给新生代(剩下的10MB分配给老年代)

    -XX:SurvivorRatio=8:新生代中Eden区与一个Survivor区的空间比例是8:1

    代码:

    public class AllocationTest {
    
        private static final int _1MB = 1024 * 1024;
    
        public static void main(String[] args) {
    
            byte[] a1, a2, a3, a4;
            a1 = new byte[2 * _1MB];
            a2 = new byte[2 * _1MB];
            a3 = new byte[2 * _1MB];
            a4 = new byte[4 * _1MB]; // 出现一次Minor GC
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    控制台打印:

    image-20230927234410189

    上述老年代占用4M跟下一节阈值默认配置有关。

    Minor GC、Major GC以及Full GC因为不同垃圾收集器以及不同JDK版本触发条件都不尽相同

    Minor GC:新生代垃圾收集

    Major GC:老年代垃圾收集

    Full GC:全堆垃圾收集

    思路基本都是内存不够用了进行垃圾收集

    大对象直接进入老年代

    大对象指需要大量连续内存空间的Java对象最典型的大对象便是那种很长的字符串,或者元素数量很庞大的数组

    Java虚拟机中要避免大对象的原因

    分配空间时,容易导致内存明明还有不少空间时就提前触发垃圾收集,以获取足够的连续空间才能安置好大对象,而当复制对象时,大对象意味着高额的内存复制开销

    -XX:PretenureSizeThreshold:指定大于该设置值的对象直接在老年代分配(默认值为3145728字节即3M

    【-XX:PretenureSizeThreshold=3145728】,超过3MB的对象都会直接在老年代进行分配。

    -XX:PretenureSizeThreshold参数仅在使用Parallel Scavenge(并行年轻代)和Parallel Old(并行老年代)【JDK8默认组合】收集器组合时以及Serial和ParNew两款新生代收集器有效。

    长期存活的对象将进入老年代

    HotSpot虚拟机中多数收集器都采用了分代收集来管理堆内存,那内存回收时就必须能决策哪些存活对象应当放在新生代,哪些存活对象放在老年代中。

    虚拟机给每个对象定义了一个对象年龄(Age)计数器,存储在对象头中

    对象通常在Eden区里诞生,如果经过第一次Minor GC后仍然存活,并且能被Survivor容纳,该对象会被移动到Survivor空间中,并且将其对象年龄设为1岁。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15),就会被晋升到老年代中

    -XX:MaxTenuringThreshold:设置对象晋升老年代的年龄阈值(默认15)

    动态对象年龄判定

    为了能更好地适应不同程序的内存状况,

    HotSpot虚拟机并不是永远要求对象的年龄必须达到-XX:MaxTenuringThreshold才能晋升老年代

    如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到-XX:MaxTenuringThreshold中要求的年龄。

    空间分配担保

    在发生Minor GC之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,大于则保证这次Minor GC是安全的,否则看参数-XX:HandlePromotionFailure:是否允许担保失败;如果允许,继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,大于,将尝试进行一次Minor GC,小于则进行一次Full GC。

    JDK 6Update 24之后规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行Minor GC,否则将进行Full GC。(-XX:HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略

  • 相关阅读:
    MCAL系列介绍04-GPT
    python趣味编程-5分钟实现一个Tic Tac Toe游戏(含源码、步骤讲解)
    Nautlius Chain主网正式上线,模块Layer3时代正式开启
    Elk-Metricbeat配置Tomcat的日志分析 (Metricbeat-part3)
    测试平台项目部署二(手动部署改成Dockerfile)
    企业服务器租用对性能有什么要求呢?
    Java多线程【锁优化与死锁】
    基于php的校园零食网站
    拷贝对象时编译器的一些优化
    线性表4 双向链表及基本操作实例
  • 原文地址:https://blog.csdn.net/qq_43417581/article/details/133420887