垃圾回收算法

1.引用计数法

描述:每一个对象都有一个引用计数器,当有引用连接到对象的时候,引用计数加一,当引用离开作用域或者被置为null的时候,引用计数减一。

虽然管理引用计数的开销并不大,但是这项开销在整一个程序的生命周期中持续发生。垃圾回收器会在含有全部对象的列表上面遍历,当发现某个对象的引用计数为0的时候,就释放这个对象的空间。但是这种方法有一个缺陷:如果某个对象在循环中被引用,可能会出现“对象应该被回收,但是引用计数不为0的情况”。引用计数法通常用来说明垃圾回收的工作方式,但是好像没有哪一个java虚拟机是采用这种方式实现垃圾回收的

2.自适应的垃圾回收技术:

  一种更快的模型:对于任何“活”的对象,一定能够最终追溯到其存活在堆栈或静态存储空间的引用。由此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。对于每发现一个引用,必须追踪到它引用的对象,然后是此对象的所有引用,如此反复进行,直到“根源于堆栈和静态存储区中所有的引用”所形成的网络全被访问为止。

在这种方式下,java虚拟机将采用一种自适应的垃圾回收技术。

自适应主要有两种方式:

1)停止—复制法:先暂停程序的运行,然后将所有存活的对象从当前的堆复制到另一个堆,没有被复制的全部都是垃圾,当对象被复制到新的堆的时候,它们是一个挨着一个的,所以新堆保证紧凑排列,然后就可以按照上面的方法简单的分配新的空间了。

当一个对象从一处搬到另一处的时候,所有指向它的那些引用必须全部修正。

对于这种所谓的“复制式回收器”而言,效率会降低,因为,第一,你要有两个堆,然后要在这两个分离的堆中操作,从而,你要维护的是比实际操作多一倍的空间。某些java虚拟机对这个问题的处理方式是这样的,按需从内存中分配几块较大的内存,复制动作发生在这些大块的内存之间。第二,如果程序处于稳定状态,那么可能只会产生少量的垃圾,甚至没有垃圾。这时候,垃圾复制式的回收器还会尽心复制操作,这显然没有必要,而且很浪费。为了避免这种情形,一些java虚拟机会进行检查,要是没有垃圾产生,那么就会切换到另一种工作模式——标记—清理模式,标记—清理的速度非常慢,但是只有少量垃圾的时候,就会很快了。

2)标记—清理法:从堆栈和静态存储区出发,遍历所有的引用,进而找到所有的存活的对象,每当找到一个存活的对象的时候,就会给这个对象设一个标记,在这个过程不会做任何的回收对象操作。只有全部标记完成之后,清理动作才开始,在清理的过程中,没有被标记的对象被释放,不会发生任何复制的动作,所以剩下的堆空间是不连续的。垃圾回收器要是希望得到连续的空间的话,就必须重新整理剩下的对象。

在这里所讨论的java虚拟机中,内存分配以较大的“块”为单位。如果对象较大,那么它会占用单独的块。严格来说,停止—复制法要求在释放旧对象之前,必须把所有存活的对象从旧堆复制到新堆,这导致大量的内存复制行为。有了块之后,垃圾回收器在回收的时候就可以往废弃的块中拷贝对象了。每一个块都有相应的代数来记录它是否存活。通常,如果块在某处被引用,其代数会增加,垃圾回收器会定期对上次回收动作完成之后新分配的块进行整理。垃圾回收器会定期进行完整的清理动作——大型对象仍然不会被复制,其代数会增加,内含小型的那一块则被复制并整理。java虚拟机会进行监视,如果对像都稳定,那么就会切换到标记—清理模式。同样,如果标记—清理的效果变差,那么会切换会停止—复制的方式。

3.标记—整理法

    和标记—清理法差不多,但是标记之后不是直接清理消亡的对象,而是让存活的对象往一边移动,然后清理掉边界之后的空间,这样操作之后,就会产生连续的堆空间。

4.分代算法:

分代的垃圾回收策略,是基于这样的事实:不同的对象生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。

年轻代(大部分存活的时间很短):

所有的新生成的对象首先都是放在年轻代的,年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。

由于对象的存活率不高

新生代内存按照8:1:1的比例分为eden区和两个survivor(survivor0,survivor1)区。
 

但是如果存活的对象超过10%怎么办?为了解决这个问题,我们会把java的堆分为两部分,一部分就是存放新生代的,另一部分就是存放年老代(对象存活的时间很长)的。

以前是没有永久代的,之前永久代是和年老代存放在一起的,但是一些类的信息很少发生变化,于是就将年老代和永久代分开,这有助于性能的提高。

以下两种情况会将对象从新生代转到年老代。

一、新生代里的每一个对象,都有一个年龄,这些对象达到一定的程度的时候(年龄就是熬过GC的次数,每一次GC,如果这个对象存活下来,年龄加一),则会被转移到年老代,这个转入的年龄值在JVM中是可以设置的。

二、在新生代存活的对象占用的内存空间超过10%的时候,多余的对象会被放入年老代。

下面来这种算法谈谈java GC机制:

这种算法分两种GC,一种是minor GC针对年轻代的GC,一种是Full GC。

当Eden满的时候,执行minor GC(采用停止—复制法),将消亡的对象清理,将剩余的对象复制到一个存活区Survivor0(两个存活区总有一个是空白的),此后每次eden区满的时候,就执行mimor GC,并将剩余的对象都添加到survivor0,当survivor0也满的时候,将其中仍然存活的对象直接复制到Survivor1,以后没执行minor GC后,就将剩余的对象添加到Survivor1,这个时候survivor0是空白的。

老年代:

由于存活的对象比较多,而且还存活一些大对象,那么在对老年代GC(Full GC)的时候,如果采用停止—复制法,那会非常的低效,老年代一般采用标记—整理法。发生minor GC之后,虚拟机会检测每一次进入老年代的大小是不是大于老年代剩余空间的大小,如果大于,就触发一次FullGC。

方法区(永久代):

永久代的回收有两种:常量池中的常量,无用的类信息,常量的回收很简单,没有引用了就可以被回收。对于无用的类进行回收,必须保证3点:

类的所有实例都已经被回收

加载类的ClassLoader已经被回收

类对象的Class对象没有被引用(即没有通过反射引用该类的地方)

永久代的回收并不是必须的,可以通过参数来设置是否对类进行回收。

补充知识点:
内存分配与回收策略


1.对象优先在Eden分配

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

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

所谓大对象是指,需要大量连续内存空间的java对象。一般来说大对象都直接进入老年区。

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

虚拟机给每一个对象定义了一个对象年龄计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存在,并且能被Survior容纳的话,将被移动到Survivor空间中,并且对象年龄设置为1,对象在survivor中,没经历一次minor GC,年龄就增加1,当年龄到一定程度的时候,就会被晋升到老年代中。

4.动态对象年龄判定

如果Survivor空间中相同的年龄所有的对象大小的总和大于Survivor空间的一半,年龄大于或者等于该年龄对象就可以直接进入老年代。

空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

前面提到过,新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在Minor GC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。与生活中的贷款担保类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多空间。

取平均值进行比较其实仍然是一种动态概率的手段,也就是说,如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure)。如果出现了HandlePromotionFailure失败,那就只好在失败后重新发起一次Full GC。虽然担保失败时绕的圈子是最大的,但大部分情况下都还是会将HandlePromotionFailure开关打开,避免Full GC过于频繁。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值