目录
前言
上一篇文章讲到对象内存的回收,本篇文章将会进一步深入记录进行垃圾对象内存回收的三种算法和不同的实现。
一、垃圾收集算法
传统的JVM通常根据对象的存活周期的不同而将堆区内存分为新生代和老年代,那么在进行垃圾回收的时候,也要考虑到两部分内存中对象的特点,分别实现不同的垃圾收集算法,以达到高效回收垃圾的目的。这种设计思维便是分代收集理论。下面三种算法便是进行垃圾回收的基础算法。
1、标记-复制算法
类似于操作系统的内存管理和磁盘管理,JVM会将堆区内存划分为若干个大小相同的块,一个对象可以占据一个或多个内存块。
标记复制算法会将内存划分为两个包含相同数量内存块的分区。其中一个分区用于存放存活的对象,为使用区,另一个分区是保留区,在进行垃圾回收的时候使用。
如上图,在回收前,算法会将垃圾对象标记为灰色。在标记完成后,算法会将存活的对象复制到保留区,并将使用区的对象全部清除,这样原来的使用区就变为保留区,原来的保留区变为使用区。
由于标记-复制算法涉及到复制操作,很显然该算法适用于每次只进行少量复制的情景,而新生代每次在进行垃圾回收时都会清除掉大量的对象,因此可以使用该算法。该算法有个缺点便是一半的内存不能够使用。
2、标记-清除算法
标记-清除算法分为标记和清除阶段。标记便是找出需要清除的对象,清除便是清除掉被标记的垃圾对象,如下图:
该算法解决了标记-复制算法的问题,但又产生了新问题,那便是在进行多次垃圾回收之后,会产生大量的内存碎片。
3、标记-整理算法
标记整理算法综合解决了上面两种算法的缺点,其中标记阶段还是使用标记-复制算法和标记-清除算法的方式,不过在清除之前加入了整理,也就是将标记的存活对象整理到内存的一端,最后的清除便是将存活对象边界之外的内存全部清空。如下图:
标记-整理算法既解决了内存只能使用一半的问题,也解决了产生内存碎片的问题。
二、垃圾收集器
垃圾收集器是垃圾回收算法的具体实现。
1、Serial收集器
Serial收集器是单线程收集器,分为新生代和老年代两个版本,JVM参数分别为-XX:+UseSerialGC
和-XX:+UseSerialOldGC
,在JDK1.5之前和Parallel收集器搭配使用。新生代使用标记-复制算法,老年代使用标记-整理算法。
什么是单线程收集器呢?我们知道,JVM在运行程序时,往往会开出多个应用程序线程,并分别为这些线程分配单独的内存空间。单线程收集器便是使用一个线程来专门进行垃圾回收,这明显会产生一个问题,那就是在垃圾回收的过程中需要暂停应用程序线程,也就是STW(Stop The World)
,这可能会严重影响到用户的体验。
2、Parallel收集器
针对Serial收集器的问题,JVM设计者们设计了一种多线程并发的收集器,适用于堆内存小于4G的机器。与Serial收集器一样,新生代使用标记-复制算法,老年代使用标记-整理算法。该收集器在进行垃圾回收时,开出多个垃圾回收线程,提升回收速度。但该收集器在垃圾回收的过程中也需要STW
,若回收的垃圾特别多,仍然会影响用户的体验。新生代和老年代收集器的JVM参数分别为-XX:+UseParallelGC
和-XX:+UseParallelOldGC
。
3、ParNew收集器
该收集器原理和Parallel收集器很类似,不同点是其能够和CMS收集器搭配使用,设置参数为-XX:+UseP