JVM-对象分配与GC

对象分配过程

1. 常规

  • 1.new的对象先放Eden区。此区有大小限制。

  • 2.当Eden的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对Eden区进行垃圾回收(MinorGC/Yong GC),将Eden区中的不再被其他对象所引用的对象(不可达对象)进行销毁。再加载新的对象放到Eden区

  • 3.然后将伊甸园中的剩余对象移动到survivor0区。

  • 4.如果再次触发垃圾回收(此时会连带着存放对象的survivor0区一块),此时上次幸存下来的放到survivor1区的,

  • 5.如果没有回收,就会放到survivor1区,如果再次经历垃圾回收,此时会把没回收的都放到survivor0区。 也就是每次回收都会把幸存下来的(Eden区和存放幸存者的survivor区)存放到空的幸存者区。

  • 6.如此往复,当一个对象存活次数达到15次,也就是对象age达到15(默认是15)就会晋升到Old区 当Old区内存不足时,再次触发GC:Major GC/Full GC(取决于是否为CMS回收器,只有CMS回收器有单收集Old区行为),进行Old区的内存清理 若Old区执行了Major GC/Full GC之后,发现依然无法进行对象的保存,就会产生OOM异常。 可以设置参数:-Xx:MaxTenuringThreshold= N进行设置

当然,以上是正常的对象的分配过程,也会有一下特殊情况

2. 动态对象年龄判断

他的大致规则就是,假如说当前放对象的Survivor区域里,一批对象的总大小大于了这块Survivor区域的内存大小的50%,那么此时大于等于这批对象年龄的对象,就可以直接进入老年代了

假设Survivor0区(100M空间大小)有两个对象,这俩对象的年龄一样,都是2岁,然后俩对象加起来内存超过了50MB,这个时候,Survivor0区里大于等于2岁的对象,都要进入老年代里去。这就是动态年龄判断的规则,这条规则也会让一些年轻代的对象进入老年代。实际这个规则运行的时候是如下的逻辑:年龄从⼩到⼤进⾏累加,当加⼊某个年龄段后,累加和超过survivor区域TargetSurvivorRatio的时候,就从这个年龄段⽹上的年龄的对象进⾏晋升。

3. 大对象直接进入老年代

意思就是如果你要创建一个大于这个大小的对象,比如一个超大的数组,或者是别的啥东西,此时就直接把这个大对象放到老年代里去,压根不会经过年轻代。之所以这么做,是因为要避免年轻代里出现那种大对象,然后屡次躲过GC,还得把他在两个Survivor区域里来回复制多次之后才能进入老年代。

4. Survivor区满了

  • 特别注意,在Eden区满了的时候,才会触发MinorGC,而Survivor区满了后,不会触发MinorGC操作

    如果Survivor区满了后,将会触发一些特殊的规则,也就是可能直接晋升Old区

     

TLAB(Thread Local Allocation Buffer)

由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的,为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域,它包含在Eden空间内。多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略

 

  • 尽管不是所有的对象实例都能够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选。可以通过选项“-Xx:UseTLAB”设置是否开启TLAB空间。

  • 默认情况下,TLAB空间的内存非常小,仅占有整个Eden空间的1%,可以通过选项“-Xx:TLABWasteTargetPercent”设置TLAB空间所占用Eden空间的百分比大小。

  • 一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存。

⚠️ 堆空间不都是共享的,因为还有TLAB这个概念,在堆中划分出一块区域,为每个线程所独占

 

Minor GC,Major GC,Full Gc,Mixed GC

Minor GC:

当年轻代空间不足时,就会触发MinorGC,这里的年轻代满指的是Eden区满,Survivor满不会引发GC。(每次Minor GC会清理年轻代的内存。)因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。

Minor GC后Eden区是空的,会把Eden中的所有活的对象都移到Survivor区域中,如果Survivor区中放不下,那么剩下的活的对象就被移到Old generation 中。

Major GC

指发生在老年代的GC,对象从老年代消失时,只有CMS回收器有单收集Old区行为

Full GC

整堆收集,收集整个Java堆和方法区的垃圾收集触发

Full GC执行的情况有如下五种:

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

  2. 老年代空间不足

  3. 方法区空间不足 (永久代或者元空间)

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

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

⚠️Full GC 是开发或调优中尽量要避免的。这样暂时时间会短一些

⚠️Major GC 和 Full GC出现STW的时间,是Minor GC的10倍以上

Mixed GC

收集整个young gen以及部分old gen的GC(只有G1有这个模式)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值