读 - 深入理解java虚拟机 - 笔记(六-2) - 垃圾收集器和内存分配策略(3章)-垃圾回收算法

深入了解虚拟机这本书也强调了,每个平台操作内存的方式不同,因此垃圾回收的程序细节不考虑,只讨论算法的思想。
1.标记清除算法


最基础的收集算法是“标记-清除”算法,算法分为“标记”-“清除”两个阶段。


1.1标记
      标记在前文中已经提及,分为两个阶段。
(1)第一次标记
当可达性分析之后发现对象并不在Gc Roots中的任何引用链上时,进行第一次标记,此时还会进行一次删选,判定此对象是否有必要执行finalize()方法。有必要执行 的会放入F-Queue队列中
(2)第二次标记
GC将对F-Queue队列中的对象进行第二次小规模标记,在其finalize()方法中重新建立关系的对象,会将其移除即将回收的集合,剩下的都是已死的对象。能进行清除。
1.2清除
  两次标记后,仍在集合内的对象统一被收回。

1.3优点

基础算法,后续算法都是基于此的不足进行改进

    1.4缺点

A).效率问题:标记和清除两个过程的效率都不高

B).空间问题:标记清除后会产生大量不连续的内存碎片,会导致分配大内存时没有连续的内存,从而触发另一次垃圾回收。

    1.5应用场景

针对老年代的CMS收集器

1.6执行过程

读 - 深入理解java虚拟机 - 笔记(六-2) - 垃圾收集器和内存分配策略(3章)-垃圾回收算法

2.复制算法
复制算法的产生在书中第二段提及,所解决的问题就是标记清除算法的效率问题

2.1思路

1.它将可用内存按内容量划分为大小相等的两块,每次只使用其中的一块。

2.当这一块的内存用完了,将还存活着的对象复制到另一块上面,使用这一块。

3.然后再把已使用过的内存空间一次清理掉。重复步骤2.

2.2优点
优点很明显,不会存在内存碎片化问题(使用“指针碰撞”的方式分配内存),因为每次都是回收整个半区内存。

关于指针碰撞,不去理会,不是本篇关注点,稍加记录指针碰撞
2.3缺点
1.缺点也很明显,空间浪费,只能使用一半的空间。不过可以从分配比例着手解决这个问题。

2.当对象存活时间很长时,会出现复制操作变多,此时效率肯定会降低。
2.4执行过程
读 - 深入理解java虚拟机 - 笔记(六-2) - 垃圾收集器和内存分配策略(3章)-垃圾回收算法
2.5应用场景
商业JVM都采用这种算法来回收新生代(但是需要改良缺点1,也即是内存分配需要按照比例来)
这边需要提及一下HosSpot虚拟机的新生代内存布局和算法

2.6HotSpot解决方案
1).将新生代内存分为一块较大的Eden空间和两块较小的Survivor空间

2).只使用Eden和其中一块Survivor空间,其中一块不使用

3).当回收时,将Eden和Survivor中还存活的对象一次性复制到另外一块Survivor空间上。

4).清理掉Eden和使用过的Survivor空间。

5).后面就使用Eden和复制到的那一块Survivor空间,重复步骤3.

6).默认分配大小是Eden:Survivor 为 8:1,剩下的10%给余下的Survivor空间,

7).那如果出现10%的那一块Survivor没有足够的空间存放上一次新生代收集下来的存活对象时,咋办?此时会存在一个分配担保机制,将这些对象存放在老年代,至于这个分配担保机制也不是本文重点,以后关注。

3.标记整理算法
3.1算法思想
标记整理算法应用于老年代,因为老年代的对象不会轻易被回收。

 3.1.1标记
标记过程就如标记删除算法一样,进过两次标记过程。

3.1.2整理
但是后续不是对可回收对象进行清理,而是让所有幸存的对象都向一端移动,然后直接清理掉边界以外的内存。

3.2算法流程
读 - 深入理解java虚拟机 - 笔记(六-2) - 垃圾收集器和内存分配策略(3章)-垃圾回收算法
3.3优点
1.不会像复制算法一样,对象存活率高了而效率变低

2.不会产生碎片,
3.4缺点
多出需要整理的过程,效率比标记清除更低了,直观上来看的话。
3.5应用场景
回收老年代使用

4.分代收集算法
4.1算法思路
主要是结合不同的收集算法处理不用的区域,这样,根据每个区域的特点可以采用不用的算法。

Java一般把堆分为新生代和老年代

4.1.1).新生代里的对象上面也有介绍,死的很多也很快,少量能够存活,因此适合复制算法。

4.4.2).老年代里的对象存活率高,没有额外的空间可以分配担保,适合标记清除或者标记整理算法。
读 - 深入理解java虚拟机 - 笔记(六-2) - 垃圾收集器和内存分配策略(3章)-垃圾回收算法
上图中的永久代其实可以认为是方法区,存储了类的信息,常量,静态变量,等等等,一般不会发生回收,并且效率比较低,因为上面的分析,类的卸载的条件还是比较苛刻的,即使如此,还是会进行回收。算法是标记整理算法。


GC发生时机

1.新生代:

1.1 所有对象创建都发生在Eden区,Eden区满后触发新生代的minor GC,将Eden区和Survivor区的存活对象复制到另一个空闲的Survivor区中。

1.2 上面的分析也提到,两个Survivor永远有一个是空的,新生代的mimor GC就是在两个Survivor区之间相互复制存活对象,直达Survivor区满为止

2.年老代:

2.1 Eden区满后会触发mimor GC将存活到Survivor区,Survivor区满后触发mimor GC将存活对象复制到年老代。

2.2 经过新生代里两个Survivor之间多次复制,任然存活的对象就是年代较老的了,就可以放入年老代,如果年老代也满了,就会触发Full GC,针对新生代和年老代以及方法区。

3.方法区(持久代)

持久代满了,会触发Full GC。