垃圾回收与算法

前边讲到 JVM 运行时内存的地方,关于新生代、老年代中 GC 垃圾回收以及垃圾回收算法,不知是否有点懵懵的,这篇一块儿了解一下垃圾回收以及垃圾回收算法。java

1、垃圾回收须要完成的三件事

  1. 哪些内存须要回收?——垃圾对象(如何断定对象为垃圾对象)
  2. 如何回收?——垃圾回收算法
  3. 什么时候回收?

1. 哪些内存须要回收?

断定为 "死" 对象,或者无用对象时即视为可回收内存。算法

如何断定为垃圾对象,在这有两个方法。微信

1.1 引用计数算法

在 Java 中,引用和对象是有关联的。若是要操做对象则必须用引用进行。所以,很显然一个简单的办法是经过引用计数来判断一个对象是否能够回收。
简单说,即一个对象若是没有任何与之关联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。app

这种方法的效率很是的高,可是却有一个很大的缺点,没法解决相互引用的对象,致使进入计数的死循环,导致引用计数算法没法通知 GC 收集器回收他们。jvm

由于这种弊端,在主流的 Java 虚拟机里面没有选用计数算法来管理内存的。spa

1.2 可达性分析算法

为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。经过一系列的“GC roots”对象做为起点搜索。若是在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。orm

要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要通过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。对象


2. 如何回收?——垃圾回收算法

2.1 标记-清除算法(几乎都不用)

该算法为最基础的垃圾收集算法,如同名称同样,该算法分为 "标记" 和 "清除" 两个阶段。blog

标记阶段标记出全部须要回收的对象,清除阶段回收被标记的对象所占用的空间。如图生命周期

从图中咱们就能够发现,该算法最大的问题是内存碎片化严重,空间碎片太多,内存不足时,很容易触发下一次 GC。

2.2 复制算法(新生代使用)

为了解决 Mark-Sweep 算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块用完了,就将还存活的对象复制到另外一块上面,而后再把已使用的内存空间一次性清理掉,每次都对整个半区进行内存回收,不须要考虑碎片问题,如图:

这种算法虽然实现简单,内存效率高,不易产生碎片,可是最大的问题是可用内存被压缩到了本来的一半。且存活对象增多的话,Copying 算法的效率会大大下降。

补充:如今商业虚拟机都是采用复制算法来回收新生代,对象在建立时,虚拟机将内存划分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和一块 Survivor , 当回收时,将 eden 和 Survivor 中还存活着的对象一次性的复制到另外一块 Survivor 空间上,而后清理掉 eden 和刚才用过的 Survivor 空间。

hotspot 默认 Eden 和 Survivor 比例为:8:1,也就是只有 10% 的空间被浪费。

2.三、标记整理算法(老年代使用)

标记整理,跟 标记-清除 算法中的标记过程是同样的,只是后续不是直接对可回收对象进行清理,而是让全部存活的对象向一端移动,而后直接清理掉端界边界之外的内存。如图:

2.四、分代收集算法

分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根据对象存活的不一样生命周期将内存划分为不一样的域,通常状况下将 GC 堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。

老生代的特色是每次垃圾回收时只有少许对象须要被回收,新生代的特色是每次垃圾回收时都有大量垃圾须要被回收,所以能够根据不一样区域选择不一样的算法。


3. 什么时候回收?

什么时候回收以前,先了解一下几种 GC。

  • Minor GC:该 GC 会清理年轻代的内存,GC 时会触发 "全世界的暂停"
  • Major GC/Full GC:清理老年代。

以下是几种 GC 的触发场景:

  1. 执行 system.gc() 的时候
  2. 老年代空间不足,GC 以后,空间不足会触发 outofmemoryError
  3. 永久代空间不足,GC 以后,空间不足会触发 java.outofMemory PerGen Space
  4. Minor GC以后 Survior放不下,放入老年代,老年代也放不下,触发FullGC,或者新生代有对象放入老年代,老年代放不下,触发FullGC
  5. 新生代晋升为老年代时候,老年代剩余空间低于新生代晋升为老年代的速率,会触发老年代回收
  6. new 一个大对象,新生代放不下,直接到老年代,空间不够,触发FullGC

2、从新疏导 JVM GC 过程。

Java 堆是垃圾收集器管理的主要区域,该区域又分为新生代、老年代,而新生代又分为一个 Eden 区和两个 Survivor 区,关于对象的建立,优先在 Eden 区中分配,当 Eden 区没有足够空间时,虚拟机将触发一次 Minor GC,因为大多数对象都是朝生夕灭,因此 Minor GC 很是频繁,速度也很是快。

Full GC,发生在老年代的 GC,当老年代没有足够的空间时即发生 Full GC,发生 Full GC 通常都会有一次 Minor GC,咱们在这将 Full GC 等价于 Major GC,也许会有疑问,Full GC 跟 Major GC 到底有什么区别呀,因为许多 Major GC 是由 Full GC 触发的,因此不少状况下将这两种 GC 分离是不太可能的,在这就不继续填坑了,感兴趣的小伙伴能够自行搜索了解。

3、最后总结

首先要清楚垃圾回收要干的三件事,哪些须要回收、如何回收以及什么时候回收。
经过 "引用计数算法"(不推荐) 或 "可达性分析算法" 来断定须要回收的对象,知道了须要哪些是须要回收的对象,再经过垃圾回收算法(复制、标记清除、标记整理、分代收集)实现垃圾回收。

补一张图:

若是文章有错的地方欢迎指正,你们互相留言交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够关注微信公众号:niceyoo

相关文章
相关标签/搜索