菜鸟学习JVM——垃圾回收算法

Java垃圾回收算法

所有的垃圾回收算法都是为了解决三个问题:

  • 哪些内存需要回收
  • 什么时候回收
  • 怎么回收

引用计数法(Reference Counting)

引用计数法原理很简单,给每个对象分配一个计数器,当被引用时就加一,引用失效就减一。计数器为零时,则说明该对象不可能再被使用。引用计数法效率不错,大部分情况下是个不错的算法,但它有一个非常明显的缺点,就是无法回收互相引用的对象,从而引起内存泄露。这也是主流Java虚拟机没有选择它的一个重要因素。
菜鸟学习JVM——垃圾回收算法_第1张图片
上图中很明显A和B已经不其他对象引用了,但他们互相引用,彼此的计数器都不为零,用引用计数法无法将其回收,从而造成了内存泄露。

标记-清除法(Mark-Sweep)

标记-清除算法是现代垃圾回收算法的思想基础,后面提到的所有算法都是基于该算法的思想。从名字就能看出,该算法分两个阶段进行——“标记”和“清除”。首先通过根对象标记所有可达的对象,然后清除所有未被标记的不可达对象。该算法有一个比较大的缺点,就是容易产生内存碎片。过多的内存碎片对于大对象的内存分配,效率非常低。回收过程如下图:
回收前
回收前
白色表示空闲,灰色表示存活对象,黑色表示垃圾对象
回收后
回收后

什么是可达?

简单的说,就是如果能通过一条路径找到该对象,那么就将该对象称为可达对象。
专业点的说法就是,通过一系列根对象(GC Roots)作为起点,向下搜索,搜索所有的引用路径——引用链(Reference Chain),只要根对象和该对象相连,那么该对象就为可达对象,反之,则为不可达的对象,则证明此对象是不可用的,需要被回收。

复制算法(Copying)

复制算法基于标记-清除算法并对其产生过多内存碎片的缺点进行了优化。复制算法将内存空间分成两等份(如下图的A和B),每次只使用其中的一块,当垃圾回收的时候,将A中的可达对象复制到B中,然后清空A中的所有对象。这样就避免了产生内存碎片的情况,但这种算法的缺点也是显而易见的,那就是太浪费空间。
回收前
A
回收后
B

标记-压缩法(Mark-Compact)

光从名字上就能看出,该算法继承自标记-清除算法。该算法在标记和清除之间又加了一个操作——压缩。首先将标记所有可达对象,然后将所有可达对象压缩(或者叫移动)到内存的一端,最后将边界以外的空间全部清空。这样既避免了产生内存碎片,又不需要空出一块内存空间,一举两得。
回收前
回收前
回收后
回收后

跟上面不同的是:这是同一块内存回收前和回收后的不同状态,而上面是A、B两块不同的内存。

总结(Summary)

JVM中采用的并不是某一种回收算法,而是多种算法组合使用,因为任何一种算法都不是完美的,都有自身的优缺点,有自己适用的场景。需要把他们放到合适的地方,这样才能各尽其能,达到一个最好的效果。

你可能感兴趣的:(JVM,Java,编程语言,架构师之路)