JVM之四种GC算法详解
目录:
- 什么是GC?
- GC算法之引用计数法
- GC算法之复制算法(Copying)
- GC算法之标记清除(Mark-Sweep)
- GC算法之标记压缩(Mark-Compact)
- GC算法之标记清除压缩(Mark-Sweep-Compact)
- 小结
1. 什么是GC?
先看图:
GC是分代收集算法
(更为专业)
- JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。
- 因此GC按照回收的区域又分两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)
Minor GC 和 Full GC的区别
- 普通GC(minor GC):只针对在新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快。
- 全局GC(major GC or Full GC):指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次的Minor GC(但不是绝对的)。Major GC的速度一般要比Minor GC慢上10倍以上。
2. GC算法之引用计数法
即当对象的引用为0的时候会被回收。
3. GC算法之复制算法(Copying)
(1)概述:
年轻代使用的是Minor GC,这种GC算法采用的是复制算法。
(2)原理
- Minor GC会把Eden中的所有活的对象都移到Survivor区域中,如果Survivor区放不下,那么剩下的活的对象就被移到Old generation中,也即一旦收集后,Eden是变成空的了。
- 当对象在Eden(包括一个Survivor区域,这里假设是from区域)出生后,在经过一次Minor GC后,如果对象还存活,并且能够被另一块Survivor区域所容纳(上面假设是from区域,这里应为to区域,即to区域有足够的内存空间来存储Eden和from区域存活的对象),则使用复制算法将这些仍然还存活的对象复制到另外一款Survivor区域(即to区域)中,然后清理所使用的Eden和Survivor(即from区域),并且这些对象的年龄设置为1,以后对象在Survivor区域每熬过一次Minor GC,就将对象的年龄+1,当对象的年龄达到某个值时(默认是15岁,通过
-XX:MaxTenuringThreshold
来设定对象在新生代中存活的次数),这些对象就会成为老年代。 - 复制算法的基本思想是将内存分为两块,每次只用到其中一块,当这一块内存用完,就将活着的对象复制到另外一块上面。
复制算法不会产生空间碎片
。
(3)复制算法的缺点:
- 浪费了一般的内存
- 如果对象的存活率很高,我们可以极端一点,假设100%存活,那么我们需要将所有对象都复制一遍,并将所有引用地址重置一遍,复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变得不可忽视。所以从以上的描述中不难看出,
复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须克服50%内存的浪费
。
4. GC算法之标记清除(Mark-Sweep)
(1)概述:
- 老年代一般是由标记清除或者是标记清除与标记整理的混合实现
(2)原理
当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。
- 标记:从引用根节点开始标记所有的被引用的对象。标记的过程其实就是遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象
- 清除:遍历整个堆,把未标记的对象清除。
(3)标记清除算法的缺点
- 此算法需要暂停整个应用,会产生内存碎片。
用通俗的话解释一下标记清除算法,就是当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将依旧存活的对象标记一遍,最终再将堆中所以没被标记的对象全部清除掉,接下来便让程序恢复运行。
(4)简单图:
5. GC算法之标记压缩(Mark-Compact)
(1)概述:
- 老年代一般是由标记清除或者是标记清除与标记整理的混合实现。
(2)原理
(3)标记压缩算法的缺点:
1.标记整理算法的唯一的缺点就是效率也不高
,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,标记整理算法要低于复制算法。
6. GC算法之标记清除压缩(Mark-Sweep-Compact)
(1)概念:
- 标记清除压缩,不属于四种GC算法。
流程图:
7. 小结