2019独角兽企业重金招聘Python工程师标准>>>
在开始之前,需要明确的一点是,这里谈到的垃圾回收算法针对的是JVM的堆内存,栈基本上不存在垃圾回收方面的困扰。
基本垃圾回收算法
1.引用计数算法(Reference Counting):
一种早期的垃圾回收算法。在此方法中,堆中的每一个对象都会有一个引用计数值,该对象每增加一个引用的时候,该计数加一,反之则减一。在执行垃圾回收时,那些引用计数值为0的对象将会被回收。这种方法的一个致命缺陷是无法处理循环带来的引用问题,即当两个对象互相引用时,它们的引用计数值永远不可能为0,导致回收算法的失效。因此,这种技术现在已经不为人所接受。
2.标记-清除算法(Mark-Sweep):
正如名字所描述的,这种算法分两个阶段,第一个是标记阶段,收集器从根节点开始遍历整棵对象引用树,标记它遇到的每一个对象;第二个是清除阶段,在第一个阶段中未被标记的对象将会被清除。
这种算法所面临的问题是会产生大量的内存碎片。为了解决这个问题,在这个算法的基础上又发展出‘停止-复制’,‘标记-整理’这两个应用较广的变种算法。
3.停止-复制算法(Stop-Copying):
这种算法将内存分成两个大小相等的区域,每次只使用其中的一个区域作为工作区域。在执行垃圾回收时,回收器遍历当前使用的区域,将正在被使用的对象复制到未被使用的区域,并进行内存整理,消除碎片,将该区域作为新的工作区域,然后清空原区域。这种算法的缺陷很明显,需要两倍的内存空间,算法执行示意图如下:
4.标记-整理算法(Mark-Compact):
收集器在执行垃圾回收时也是分两个阶段执行,第一个阶段回收器会遍历并标记所有的对象引用树,第二个阶段遍历整个堆内存,清除所有未被标记的对象并将被标记的对象整理到堆中的某一个区域,从而避免内存碎片的问题。图:
上面这四种是最基本的垃圾回收算法,在这四种算法思想之上发展出来其余算法,个人认为更多的是一种内存管理策略,甚至可以看成就是基本算法的组合应用,就像加减乘除与方程式的关系一样,因此将其分开描述。
渐进式算法
这类算法一般由基本算法组成,其核心思想是将内存分区域进行管理,在不同的区域和不同的时间采用不同的内存管理策略,从而避免因全系统的垃圾回收导致程序长时间暂停。这类算法一般有以下几种:
1.火车算法
这种算法把成熟的内存空间划为固定长度的内存块,算法每次在一个块中单独执行,每一个块属于一个集合。此算法的具体执行步骤较复杂,且没有具体的应用场景,在此不浪费笔墨,有兴趣的同学可以自己研究之。
2.分代收集算法
这种算法在sun/oracle公司的Hotspot虚拟机中得到应用,是java程序员需要重点关注的一种算法。
这种算法是通过对对象的生命周期进行分析后得出的,它将堆内存分成了三个部分:年青代,年老代,持久代(相当于方法区)。处于不同生命周期的对象被存储于不同的区域,并使用不同的算法进行回收。这种算法的具体执行细节将会在后续的学习笔记中详细介绍。
从网上得到的资料来看,应该还有一种增量收集算法,但是描述过于模糊,且没有标明出处,查不到相关资料,marker下,回头如有发现再回来补之。
另外需要特别注意的是利用多线程实现的堆垃圾回收技术,这方面资料来源于网络,摘抄如下:
1.串行收集
串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。
适用情况:数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。
缺点:只能用于小型应用。
2.并行收集
并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。
适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。
缺点:应用响应时间可能较长 。
3.并发收集
相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。
适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。