1.分代收集理论

  1.1分代收集理论假说

  1.2分代收集理论奠定的垃圾收集器原则

  1.3基于分代收集理论的内存划分-跨代引用假说

2.垃圾回收

3.垃圾收集算法

  3.1标记-清除算法

  3.2标记-复制算法

  3.3标记-整理算法

  

 

1、分代收集理论

  当前商用虚拟机的垃圾收集器,大多采用的是分代收集理论的设计原则。它实际上是一套符合大多数程序实际运行情况的经验算法。

 

1.1分代收集理论假说

  它建立在以下假说上

  1)弱分代假说:大多数对象都是朝生夕死

  2)强分代假说:熬过越多次垃圾收集的对象越难以死亡

  

1.2分代收集理论奠定的垃圾收集器原则

  1)收集器应该将内存区域划分为不同的区域

  2)将对象按照年龄(熬过垃圾回收的次数)分到不同的区域中去

  一个区域中大多数对象都是朝生夕死,那么就可以只关注存活的对象,而不用去标记大量要被回收的对象,提高效率

  一个区域中都是熬过多次垃圾回收的对象,那么就可以使用较低的频率去回收这个区域

 

1.3基于分代收集理论的内存划分-跨代引用假说

  设计者一般会至少把内存区域划分为新生代和老年代两个区域。但是分代收集也不仅仅是划分区域这么简单。对象不是孤立的,他们之间会相互引用,不同区域的也有相互引用。如果我们想要对新生代进行垃圾回收,我们不得不去老年代去看是否有新生代的引用。所以就有了跨代引用假说跨代引用相对于同代引用来说只占极少数

  根据这个假说我们不应该为了少量的跨代引用去扫描整个老年代。只需要在新生代上建立一个全局的数据结构-记忆体

  这个结构把老年代划分为若干个小块,标识出哪一块内容存在跨代引用。这样子只需要扫描存在跨代引用的区域即可。

  这样子做的缺点就是在对象的引用关系发生变化时,需要去维护这个数据结构。

 

2.垃圾回收

  基于分代收集理论和内存划分,产生了多种的垃圾收集模式

1)部分收集

  新生代收集(Minor GC/YoungGC):目标只是新生代

  老年代收集(Major GC/Old GC):目标只是老年代

  混合收集(Mixed GC):整个新生代和部分老年代。目前只有G1收集器有这种模式

3)整堆收集:收集整个JAVA堆和方法区

 

3.垃圾收集算法

垃圾收集算法可分为两类:引用计数式垃圾收集和追踪式垃圾收集。JAVA虚拟机只涉及第二种。下面也只讲第二种。

 

3.1标记-清除算法

  该算法分为两个部分,标记与清除。标记需要回收的对象,回收,或者标记不需要回收的对象,回收未标记的对象。
  

 

 

 
    优点:显然可见,该方法十分之简单,而且很高效,很快,因为对比其他方法,都是要标记的,而在清除阶段,也不需要执行什么抹掉内容的操作,只用把要“清除”的内存空间设为可用即可,可以说毫不费力,所以很快。
    缺点:缺点也很明显,看上面我们经过标记-清除算法的堆内存空间,可用的区域根本就不连续,存在大量的内存碎片,如果需要分配一个大空间的内存而找不到不够大小的连续内存,就会导致又一次的垃圾回收

 

3.2标记-复制算法

3.2.1简介

  这个算法实际是对上面的标记-清除算法的一种改进。解决内存不连续的问题。
  将内存区域分成大小相同的两块,每次只使用其中一个区域,当进行垃圾回收时,将存活的对象复制到另一个区域上(连续存放),然后把已经使用过的区域一次性全部清除。

 

 

 
    优点:解决了标记-清除算法内存不连续的问题,可以看到,这里内存是十分连续的,然后,其速度也还是可以的,虽然比不上标记-清除,但还是比标记-整理快很多很多
    缺点:可用内存只有一半,十分浪费内存空间。

  现在商用虚拟机大多采用这种算法去回收新生代

3.2.2Apple式回收

  Andrew Apple对该算法作出了优化,形成了Apple式回收。

  因为研究显示,新生代中有98%的对象熬不过第一次收集。基于这个现象,Apple式回收的具体做法是,把内存区域分为一块大的Eden和两块小的Survivor。Eden和其中一块Survivor正常使用,另外一块作为用来复制存活对象的区域。它们的大小是8:1:1.Eden为8,两个Survivor分别为一。这样子有效的使用的内存空间就达到了90%。但是这样子也存在问题,如果存活的对象过多,超过了Survivor的大小,就存在问题。所以,还有一个充当备用的内存(大多数在老年代)。

  HotSpot的Serial、ParNew等新生代收集器都是采用这种方式



3.3标记-整理算法

  标记复制算法适用于对象朝生夕死的新生代,却不适用于老年代。因为它的对象大多一致存活下去。针对老年代就有了标记-整理算法

  标记-整理算法不是直接对可回收对象进行清除,而是让所有存活对象都向内存空间一端移动,然后清除边界以外的内存。

 
  进行标记整理时,jvm会将灰色的都往前移,让黑色的都在灰色后面,然后以最后一个灰色的为边界,将边界外的内存空间清除掉。
  可以看到,经过标记-整理算法的堆内存变的整整齐齐,干干净净,也就是内存连续了,
       优点:保证了堆内存的连续,不会浪费堆内存
       缺点:首先,要在内存中移动对象,这很慢很低效,并且,在移动时,需要全程暂停应用程序。

  和标记清除算法相比,它多了个移动对象内存的步骤。两者都存在弊端,前者不考虑移动对象内存,那么内存碎片化就需要依赖更为复杂的内存分配器和内存访问其来解决。而移动对象内存又需要暂停程序。从整个程序的吞吐量来说,还是移动内存效率更高。

  程序的吞吐量是赋值器()和收集器效率的总和。不移动对象会使收集器的效率提升,但是内存分配和访问效率降低了。HotSpot虚拟机里面关注吞吐量的Oarallel Old收集器是基于标记整理算法的,而关注延迟的CMS收集器则是基于标记清除算法的。

  还可以两者混合使用,平时使用标记清除算法,当内存碎片化影响到大内存的分配时,采用标记整理算法。CMS就是采用的这种方法