JVM中GC算法

 

1.什么是垃圾回收

程序的运行必然需要申请内存资源,无效的内存资源如果不及时的处理就会一直占用内存资源,最终导致内存溢出,所以内存资源的管理就非常有必要的。

2.垃圾回收常见的算法

2.1 引用计数法

JVM中GC算法

 

JVM中GC算法

相互引用

原理:

假如有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器就会增加1,当引用失败时候,对象A的引用就减去一,如果对象的A计数器的值为0,就说明对象A没有引用了,可以被回收。

优点:

  • 实时性比较高,无须等到内存不够时候才开始回收,运行时可以根据对象的计数器是否为0,就可以进行回收。
  • 在垃圾回收过程中,应用哦无需挂起,入股申请内存时,内存不够,就会报outofmember错误。
  • 区域性,更新对象计数器时候,只是影响到该对象的,不会扫描的全部对象。

缺点

  • 每次对象被引用时后,都需要对计数器进行更新,有一定的时间开销。
  • 浪费cpu资源,即使内存够用,仍然在运行时进行计数器的统计。
  • 无法解决循环引用的问题。
  •  

2.2 标记清除法

标记清除法是将垃圾回收分为了两个阶段,分别是标记和清除。

  • 标记:从根节点开始标记引用的对象。
  • 清除:未被标记的对象就是垃圾对象,可以被清理。

JVM中GC算法

这个图表示了程序运行时的所有对象的状态,他们的标志位全部是0,也及时未被标记,以下默认0就是未被标记的,1为已经被标记。假如这会有效内存空间耗尽了,JVM将会停止程序运行并开启GC线程,然后开始进行标记工作,按照根搜索算法,标记完以后,对象的状态如下:

JVM中GC算法

可以看到:根据根搜索算法,所有从root对象的可达的对象都别标记了为存活的对象,此时已经完成了第一阶段的标记,就要执行清除了,那么清除完了以后,剩下的对象以及对象的状态如下图所示:

JVM中GC算法

可以看到没有被标记的对象会被回收清除掉,被标记的对象将会留下,并且会将标记重新归为0,接下来不用说了,唤醒停止的程序先线程,让程序继续运行。

此时:标记清除算法解决了引用计数法中循环引用的问题,没有从root节点引用的对象会被回收。

优缺点:

  • 效率低,标记和清除两个动作都需要进行遍历所有的对象,并且在GC时候,需要停止应用程序,对于交互性要求比较高的应用而言,体验比较差
  • 通过标记清除法清理出的内存,碎片化比较严重,因为被回收的对象可能存在于各个角落,清理出来的内存时不连贯的

 

2.3 标记压缩算法

标记压缩算法时在标记清除的基础上,做了优化改进的算法,和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一段,然后清理边界以外的垃圾,从而解决了碎片化的问题。

JVM中GC算法

 

优缺点:

同标记清除算法解决了碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有一定影响。

 

2.4 复制算法(年轻代内存空间算法)

JVM中GC算法

     1.在GC开始的时候,对象只会存在于Eden区和名为“from” 的Survivor区,Survivor区“to”是空的,

     2.紧接着进行GC,Eden区中的所以存活对象都会复制到“to”,而在from区中,仍然存活的对象会根据他们的年龄值决定他们的去向,年龄到达一定的阈值(年龄阈值可以通过:-XX:MaxTenuringThreshold来进行设置)的对象移动到老年代中,没有达到阈值的对象会被复制到“to”区

     3.进过这次GC时候,Eden区和From区已经被清空,这个时候from区和to区会进行交换他们的角色,也就是新的To区就是上次GC前的From,新的From是上次GC前的To区,不管如何,否要保证to区的Survivor是空的

    4.GC会一直重复这样的过程,直到to区被填满,to区被填满以后,会将所有对象移动到老年代当中。

优点:

  • 在垃圾多的情况下,效率比较高
  • 清理后,内存没有碎片

缺点:

  • 垃圾少的时候不适用,如:老年代内存
  • 分配到两块内存空间,在同一时刻,只能适用一半,内存使用效率比较低。

 

2.5 分代算法

前面介绍了多种回收算法,每一种都有自己的优缺点,谁都不能代替谁,所以根据垃圾回收对象的特点,进行选择,才是明智的选择。

分代算法其实就是这样的,根据回收对象的特点进行选择,在jvm中,年轻代的适合使用复制算法,年老代适合使用标记清除算法或者标记压缩算法。