JVM对象的生命周期(二)

 

 


前言

上篇文章谈到了对象是如何进入到堆中的,今天这篇文章继续探讨对象的生命周期

 

一、堆的内存划分

03a49630b702429d94f38ef92bae70f8.png

 堆里面分为年轻代和老年代,分别占堆内存的1/3和2/3|

对于年轻代,当空间占用达到整个年轻代大小的60%时会触发Minor GC。

而对于老年代,则依旧是当空间占用达到整个堆大小的70%(可以通过参数-XX:MaxHeapFreeRatio来控制)时会触发Full GC。

一般MinorGC会相对FullGC会快很多,如何减少FullGC也是我们JVM优化的重点 ,因为MinorGC主要堆年轻代进行回收,内存小,而fullGC是对整个堆的内存进行回收,而且年轻代和老年代默认采用的算法不同,年轻代一般采用复制算法,老年代采用标记整理算法

二、垃圾回收算法

1.标记-复制算法

为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

db156d7db1a4489b9fe796b3657443a2.png

 

2.标记-清除算法

算法分为“标记”和“清除”阶段:标记存活的对象,统一回收所有未被标记的对象(一般选择这种);也可以反过来,标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,比较简单,但是会带来两个明显的问题:
1.效率问题(如果需要标记的对象太多,效率不高)
2.空间问题(标记清除后会产生大量不连续的碎片)

cc5e85efedbf4097813e68536f033fa8.png

3.标记-清除算法

根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

24857ee682d24b7d830846540b74f3e4.png

 

三、执行流程

在年轻代达到一定阈值后,会进行一次minorGC
GC过程:如果对象在一次GC后仍存活,会从Eden进入年轻代的S1区域,同时GC年龄加1,再GC后仍存活会放入S2,然后在S1和S2里面往复,当年龄达到15时就会进入老年代,而老年代在内存达到阈值时就会进行FullGC

 

四、垃圾回收算法的选择

1.年轻代(标记-复制算法):JVM里面大多数对象都是用完即丢的,所以minorGC一般会发生的比较频繁,那这个时候就需要效率相对高的算法——标记-复制算法,复制算法的缺点很明显,就是内存的利用效率不高,需要预留内存来存储存活对象,根据大多数对象不会存活的逻辑,其实只需要预留一小块对象给存活对象就行,同时,Survivor区的作用是存储在上一次Minor GC中幸存下来的对象,而且每次Minor GC之后,所有Survivor区里的对象都会被移动到另外一个Survivor区或者老年代中。如果Survivor区过大,那么每次GC操作时需要复制的对象也会增多,耗费更多时间和空间。于是,年轻代中的内存占比为8:1:1,当然这是一个经验值,有需要可以自己做调整

2.老年代(标记-整理算法):老年代的对象生命周期相对较长,而且,大多数对象是不会被回收掉的,如果采用复制算法,需要复制的对象就会很多,效率就会降低;如果使用标记清除算法,则会产生很多不连续的内存空间,导致内存分配时需要遍历整个堆来查找足够大的连续空间,从而降低了垃圾回收的效率和性能。而且,上篇文章提到过,大对象会直接进入老年代,大对象意味着要大片的连续存储空间,所以选择的算法要能够清理内存碎片——标记-整理算法

 

 

五、如何确定对象是否可以被回收

JVM提供了两种算法来确定对象是否为垃圾对象

1.引用计数法

这种⽅式是给堆内存当中的每个对象记录⼀个引⽤个数。有引用就+1,引⽤个数为0的就认为是垃圾。这是早期JDK中使⽤的⽅式。引⽤计数⽆法解决循环引⽤的问题。

引用计数算法也存在一些问题。例如,在多线程环境中,如果多个线程同时修改同一个对象的引用计数,就可能出现竞争问题

1.可达性算法

该算法从一组被称为“GC Roots”的根对象开始遍历内存中的所有对象,并标记出所有能够被访问到的对象。未被标记的对象则会被认为是无用的垃圾对象,可以被回收。

以下四种情况下的对象被视为“GC Roots”,即垃圾回收器扫描的起始点:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中引用的对象

如果一个对象与任意“GC Roots”都没有关联,则被认为是不可达的,即它已经死亡,可以被回收。而如果一个对象至少与一个“GC Roots”存在关联,则被认为是可达的,不能被回收。

五、循环引用对象如何回收

当两个对象之间存在相互引用的情况时,即使它们与“GC Roots”之间没有直接联系,也不会被判定为不可达。此时,JVM需要通过其他方式来确定这些对象是否为垃圾对象。

在使用基于可达性分析算法的垃圾回收器中,JVM会遍历所有已经标记为可达(reachable)的对象,并将其保留下来,未被标记的则认为是垃圾对象(garbage)。而对于循环引用的情况,如果两个或多个对象彼此之间形成了一个环状结构,那么它们的引用计数都不会降为零,因此无法通过引用计数算法判断这些对象是否为垃圾对象。

针对循环引用的问题,一种常见的解决方案是使用弱引用(weak reference)机制。弱引用指向的对象只有在存在强引用(strong reference)同时指向该对象时才会被保留下来,在其他情况下,即使存在相互引用,也可以被垃圾回收器自动清理掉。这样就能够有效地处理循环引用的情况


 如果觉得有用,欢迎点赞收藏,一起学习一起进步。

总结

这篇文章对于堆中垃圾回收机制做了简单的阐述,并对堆中分代模型下选择不同的垃圾回收算法做了解释,下篇文章将继续探讨一下各种垃圾回收器,比如:Parallel GC,CMS,G1等

 


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值