【001】垃圾回收的算法与实现

GC的定义

GC是Garbage Collection 的简称,中文称为"垃圾回收"。

在现实世界中,说到垃圾,指的是那些不读书、不穿的衣服等。这种情况下的”垃圾“指的是自己不用的东西。

在GC中,"垃圾"的定义也是如此。GC把程序不用的内存空间视为垃圾。

GC要做两件事

 1.找到内存空间里的垃圾

2.回收垃圾,让程序员能再次利用这部分空间

满足这两项功能的程序就是GC。

GC的常见算法

 1.1960年,McCarthy在其论文中首次发布了GC算法 GC标记-清除算法。

2.1960年 George E. Collins 在论文中发布了叫做引用计数的GC算法

引用计数法有个缺点,就是它不能回收"循环引用"。

循环引用:两个及两个以上对象循环互相引用。

3.1963 年 人工智能之父 Marvin L.Minsky 在论文中发布了复制算法。

GC复制算法把内存分成了两部分

   从50年前GC算法首次发布以来,众多研究者对其进行了各种的研究,因此许多GC算法也得以发布。

    但事实上,这些算法只不过是把前文中提到的三种算法进行组合或应用。也可以这么说1963年GC赋值算法诞生时,GC的根本性内容就已经完成。

未知的第四种算法

 现在为世人所知的GC算法,不过是从之前介绍的三种基本算法中衍生出来的产物。

   现在我们使用的多数编程语言都搭载有GC。

    如下:Lisp、JAVA 、Ruby、Python、Perl

对于编程语言来说,GC就是一个无名英雄,默默地做着贡献。打个比方,天鹅在睡眠优雅的游动时,实际上脚丫子却在水下拼命地划着谁。GC也是如此,在由编程语言构造的美丽的源代码这片水下,GC在拼命的将垃圾回收再利用。

  GC算法的性能评价标准

评价GC算法性能时,我们采用以下4个标准。

 1.吞吐量

   从一般意义上来讲,吞吐量指的是"在单位时间内的处理能力",在这点在GC的世界中也不例外。

在mutator整个执行过程中,GC一共启动了3次,我们把花费的时间分别设置为A、B、C。也就是说,GC总共花费时间为(A+B+C)。另一方面,我们前面提到过,以GC为对象的堆大小是HEAP_SIZE。也就是说,在大小为HEAP_SIZE的堆进行内存管理,要花费的时长为(A+B+C)。因此,这种情况下GC的吞吐量为HEAP_SIZE/(A+B+C) 

当然,人们通常都喜欢吞吐量高的GC算法。然而判断各算法吞吐量的好坏时不能一概而论。

 打个比方,众所周知GC复制算法和GC标记-清除算法相比,活动对象越少吞吐量越高,这是因为GC复制算法只检查活动对象,而GC标记-清除算法则会检查所有的活动和非活动的对象。

然而,随着活动对象的增多,各GC算法表现出来的吞吐量也会相应的变化。极端情况下,设置出现GC标记-清除算法比GC复制算法表现的吞吐量更高的情况。

也就是说,即便是同一GC算法,其吞吐量也是受mutator的动作左右的。评价GC算法的吞吐量时,有必要把mutar的动作也考虑在内。

 

 

 

2.最大暂停时间

      最大暂停时间值得是"因执行GC而暂停执行mutator的最长时间"。这么说可能比较难理解,请再看一遍上图,最大暂停时间是A到C的最大值,也就是B。

那么,我们在合众情况下需要重视此种指标呢?

典型例子是两足步行的机器人。如果在其步行过程中启动GC,我们对机器人的控制就会暂时中断,知道GC执行完毕方可重启。也就是说,在这期间机器人完全不能运作。很显然机器人会摔倒。

再举个例子,电商类的Web项目也会面临着这种问题,所以我们不希望执行过程中长时间收到GC的影响。

这种情况下就需要缩短最大暂停时间。然而不管尝试哪种GC算法,我们都会发现较大的吞吐量和较短的最大暂停时间不可兼得。所以应根据执行的应用所重视的指标的不同,来分别采用不同的GC算法。

 

 

 

3.堆使用效率

根据GC算法的差异,堆使用效率也大相径庭。左右堆使用效率的因素有两个。

一个是头的大小,另一个是堆的用法。

首先是头的大小。在堆中对方的信息越多,GC的效率也就越高,吞吐量也就随之得到改善。但毋庸置疑,头越小越好。因此为了执行GC,需要把在头中堆放的信息控制在最小限度。

         其次,根据堆的用法,堆使用效率也会出现巨大的差异。举个例子,GC复制算法那中将堆二等分,每次只使用一半,交替进行,因此总是只能利用堆的一半。相对而言,GC标记-清除算法和引用计数法就能利用整个堆。

撇开这个不说,因为GC是自动内存管理功能,所以过量占用堆就成了本末倒置。与吞吐量和最大暂停时间一样,堆使用效率也是GC算法的重要评价指标之一。

然而,堆使用效率和吞吐量,以及最大暂停时间不可兼得。简单地说就是:可用的堆越大,GC运行越快;相反,越想有效的利用有效的堆,GC花费的时间就越长。

4.访问的局部性

   pc上有4种存储器,分别是寄存器、缓存、内存、辅助存储器。他们直接有着如下层级关系。

 

    

众所周知,越是可实现高速存取的存储器容量就越小。毫无疑问,我们都希望尽可能的利用较高速的存储器,但由于高速的存储器容量小,因此通常不可能把所有要利用的数据都放在寄存器和缓存里。一般我们会把所有的数据都放在内存里,  当CPU访问数据时,仅把要使用的数据从内存读取到缓存。与此同时,我们还将它附近的所有数据都读取到缓存中,从而压缩读取数据所需要的时间。

另一方面,具有引用关系的对象直接通常很可能存在连续访问的情况。这在多数程序中都很常见,称为"访问的局部性"。考虑到访问的局部性,把具有引用关系的对象安排在堆中较近的位置,就能提 高在缓存中读取到想利用的数据的概率,令mutator高速运行。想深入了了解访问的局部性的读者,请参考《计算机组成与设计:硬件、软件接口》       

有些GC算法会根据引用关系重排对象。

     本文参考《垃圾回收的算法与实现》

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值