垃圾回收器如何工作

垃圾收集的工作方式

(1)引用计数:

引用计数是一种简单但速度很慢的垃圾回收计数,每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加一。当引用离开作用域或被置为null时,引用计数减一。

管理引用计数会在整个程序生命周期中持续发生。如果对象之间存在循环引用,可能会出现“对象应该被回收,但引用计数却不为零”的情况。引用计数常用来说明垃圾收集的工作方式,但似乎从来未被应用于任何一种Java虚拟机中。

(2)追踪

对任何“活”的对象,一定能最终追溯到其存活在栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,如果从栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。对于发现每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复进行,直到“根源于栈和静态存储区的引用”所形成的网络全部被访问为止。你所访问过的对象都必须是“活”的。

这就解决了“交互自引用的对象组”的问题

如何处理找到的存活对象,取决于不同的Java虚拟机实现

(1)停止-复制(stop-and-copy)

显然意味着,先暂停程序的运行(所以它不属于后台回收模式),然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的都是垃圾。当对象被复制到新堆时,它们是一个挨着一个的,所以新堆保持紧凑排列,然后就可以按前述方法简单、直接分配空间了。

当把对象从一处搬到另一处时,所有指向它的引用都必须修正。位于栈或静态存储区的引用可以被直接修正,但是还有其他指向这些对象的引用,它们在遍历的过程中才能被找到(可以想象成有个表格,将旧地址映射至新地址)。

效率低原因

  1. 两个堆来回捣腾,需要维护比实际需要多一倍的空间。
    某些Java虚拟机对此问题的处理方式是,按需从堆中分配出几块较大的内存,复制动作发生在这些大块内存中(分代)

  2. 程序进入稳定状态后,产生少量垃圾甚至没有垃圾,所有内存都从一处复制到另一处浪费。为了避免这种情况
    一些Java虚拟机会进行检查:要是没有新垃圾产生,就会转换到另一种工作模式(自适应)。这种模式称为标记-清扫(mark-and-sweep)

(2)标记-清扫(mark-and-sweep)

对一般用途而言“标记-清扫”方式速度相当慢,但是当你知道只会产生少量垃圾甚至不会产生垃圾时,它的速度就很快了。
“标记-清扫”所依据的思路同样是从栈和静态存储区触发,遍历所有的引用,进而找出所有存活的对象。每当它找到一个存活对象,就会给对象设一个标记,这个过程中不会回收任何对象。只有标记工作完成时,清理动作才会开始。
在清理过程中,没有标记的对象将被释放,不会发生任何复制动作,所以剩下的堆空间是不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。

“标记-清扫”工作也必须在程序暂停的情况下才能进行

(3)Java虚拟机采用的:“自适应的、分代的、停止-复制、标记-清扫”式垃圾回收器

在这里所讨论的Java虚拟机中,内存分配以较大的“块”为单位。如果对象较大,它会占用单独的块

“停止-复制”要求释放旧对象之前,必须先把所有存活对象复制到新堆。有了块以后,垃圾回收器在回收的时候就可以往废弃的块里拷贝对象了。每个块都有相应的代数(generation count)来记录它是否还存活。通常,如果块被某处引用,其代数会增加;垃圾回收器将对上次回收动作之后新分配的块进行整理(分治)。这对处理大量短命的临时对象很有帮助。垃圾回收器会定期进行完成的清理动作——***大型对象仍然不会被复制(只是其代数会增加),内含小型对象的那些快则被复制并整理(分代)***。

Java虚拟机会进行监视,如果所有对象都很稳定,垃圾回收器的效率降低的话,就切换到“标记-清扫”方式;同样,Java虚拟机会跟踪“标记清扫”的效果,要是堆空间出现很多碎片,就会切换回“停止-复制”方式。这就是“自适应”技术

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值