JVM中垃圾收集机制

一:对象已死吗?

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进回收前,第一件事就是确定这些对象之中还有哪些对象存活,哪些已经死去(即不可能再被任何途径使用的对象)

二:判定垃圾是否存活办法

1:引用技术算法:
为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题。
2:可达性分析算法:
从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。如图所示,对象object5,object6,object7虽然相互有关联,但是他们到GC Root是不可达的,所以他们将会判定为是可回收的对象。
JVM中垃圾收集机制
注意:在java语言中,可作为GC Roots的对象包括下面几种:
a:虚拟机栈(栈帧中的本地变量表)中引用的对象
b:方法区中类静态属性引用的对象
c:方法区中常量引用的对象
d:本地方法栈中JNI(即一般说的Native方法)引用的对象

3:JVM中的永久代中会发生垃圾回收吗
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区
(译者注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)

三:垃圾收集算法:

3.1:标记清除算法
标记无用对象,在标记完后统一进行清除回收。
**缺点:标记和清除两个过程效率不高,零一个是空间问题,标记清除后产生大量不连续的内存碎片,空间碎片太多会导致以后程序运行过程中需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作。
**具体执行过程如图所示:
JVM中垃圾收集机制
3.2:复制算法:
为了解决标记-清除算法的效率不高的问题,产生了复制算法。它把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾收集时,遍历当前使用的区域,把存活对象复制到另外一个区域中,最后将当前使用的区域的可回收的对象进行回收。
优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制
。具体执行过程如图所示:
JVM中垃圾收集机制
3.3:标记-整理算法:
在新生代中可以使用复制算法,但是在老年代就不能选择复制算法了,因为老年代的对象存活率会较高,这样会有较多的复制操作,导致效率变低。标记-清除算法可以应用在老年代中,但是它效率不高,在内存回收后容易产生大量内存碎片。因此就出现了一种标记-整理算法(Mark-Compact)算法,与标记-整理算法不同的是,在标记可回收的对象后将所有存活的对象压缩到内存的一端,使他们紧凑的排列在一起,然后对端边界以外的内存进行回收。回收后,已用和未用的内存都各自一边。
优点:解决了标记-清理算法存在的内存碎片问题。
缺点:仍需要进行局部对象移动,一定程度上降低了效率
。示意图如图所示:
JVM中垃圾收集机制
3.4:分代算法:
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量的存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外空间对它进行分配担保,就必须使用标记清除或者标记整理算法来进行回收
新生代基本采用复制算法,老年代采用标记整理算法