JVM-垃圾回收算法
Posted xiaohaigui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM-垃圾回收算法相关的知识,希望对你有一定的参考价值。
垃圾回收算法
- 标记-清除算法
- 复制算法
- 标记-整理算法
- 分代收集算法
标记-清除算法
算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,效率也很高,但是会带来两个明显的问题:
- 效率问题
- 空间问题(标记清除后会产生大量不连续的碎片)
碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作,增加内存回收的次数
复制算法
为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。
很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。
标记-整理算法
根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法
分代回收算法实际上是把复制算法和标记整理法的结合,并不是真正一个新的算法
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
由于老年代的特点是每次回收都只回收少量对象,一般使用的是标记-整理算法。
分代回收法详解
上面用到的复制算法,按照前面讲的,按照空间1:1分配的,但是分代算法中不是这样的,而是按照8:2 (4:1)的比例进行分配的,其中后面一块又等分成了两块,也就是8:1:1的比例,8为新生代(Eden区),1分别为两块等大小的Survivor区。
所有新创建的对象都是放在Eden区的,所以有很多的临时变量,故每次大部分回收的垃圾都是在Eden区的,所以Eden区会分配那么多的空间,那为什么要分两块Survivor区呢?
为什么要分两块Survivor区
是因为,在进行一次coping算法回收时将Eden区中存活的对象复制到Survivor区,然后在进行一次回收时,Survivor区的存活对象没地方存放了,因为Eden区每次都有新创建的对象存在。故在新建一块Survivor B区,这时将Eden和Survivor A区存活的对象放在B区,然后清空Eden和Survivor A区。这样就相当于A和B每次回收后都有一个是全新的也就是空的,就是为了循环这种操作。
流程详解
- 新创建一个对象,默认是分在Eden区的,当Eden区内存不够时,触发一次minor gc (新生代回收),Eden区存活对象放入Survivor A区,然后新对象放入Eden区。
- 再新建一个对象,放入Eden区发现又满了,在进行minor gc,这时将Eden区和Survivor A区存活的对象移动到Survivor B区,然后清空Eden和Survivor A。
- 如果再来一个新对象,Eden区又满了,则再进行minor gc,移动到A区,然后清空Eden和B区,如果多次这样,有些长久的对象就会在不断的Survivor A和Survivor B区之间来回移动,多次之后,虚拟机默认15次后,就会将这些对象全部移动到老年区区。
- 如果新对象在eden区放不下,则直接放入老年代
- 如果Survivor 区放不下的对象,也直接放入老年代
- 发现老年代也满了,则进行一次Full gc (major gc)
以上是关于JVM-垃圾回收算法的主要内容,如果未能解决你的问题,请参考以下文章