常用四种垃圾回收算法
常用的垃圾回收算法有四种: 标记 - 清除算法, 复制算法, 标记 - 整理算法, 分代收集算法.
1. 标记 - 清除算法
分为标记和清除两个阶段, 首先标记出所有需要回收的对象, 标记完成后统一回收所有被标记的对象, 如下图.
缺点: 标记和清除两个过程效率都不高; 标记清除之后会产生大量不连续的内存碎片.
image
2. 复制算法
把内存分为大小相等的两块, 每次存储只用其中一块, 当这一块用完了, 就把存活的对象全部复制到另一块上, 同时把使用过的这块内存空间全部清理掉, 往复循环, 如下图.
缺点: 实际可使用的内存空间缩小为原来的一半, 比较适合.
image
3. 标记 - 整理算法
先对可用的对象进行标记, 然后所有被标记的对象向一段移动, 最后清除可用对象边界以外的内存, 如下图.
image
4. 分代收集算法
把堆内存分为新生代和老年代, 新生代又分为 Eden 区, From Survivor 和 To Survivor. 一般新生代中的对象基本上都是朝生夕灭的, 每次只有少量对象存活, 因此采用复制算法, 只需要复制那些少量存活的对象就可以完成垃圾收集; 老年代中的对象存活率较高, 就采用标记 - 清除和标记 - 整理算法来进行回收.
image
在这些区域的垃圾回收大概有如下几种情况:
大多数情况下, 新的对象都分配在 Eden 区, 当 Eden 区没有空间进行分配时, 将进行一次 Minor GC, 清理 Eden 区中的无用对象. 清理后, Eden 和 From Survivor 中的存活对象如果小于 To Survivor 的可用空间则进入 To Survivor, 否则直接进入老年代);Eden 和 From Survivor 中还存活且能够进入 To Survivor 的对象年龄增加 1 岁 (虚拟机为每个对象定义了一个年龄计数器, 每执行一次 Minor GC 年龄加 1), 当存活对象的年龄到达一定程度(默认 15 岁) 后进入老年代, 可以通过 -XX:MaxTenuringThreshold 来设置年龄的值.
当进行了 Minor GC 后, Eden 还不足以为新对象分配空间(那这个新对象肯定很大), 新对象直接进入老年代.
占 To Survivor 空间一半以上且年龄相等的对象, 大于等于该年龄的对象直接进入老年代, 比如 Survivor 空间是 10M, 有几个年龄为 4 的对象占用总空间已经超过 5M, 则年龄大于等于 4 的对象都直接进入老年代, 不需要等到 MaxTenuringThreshold 指定的岁数.
在进行 Minor GC 之前, 会判断老年代最大连续可用空间是否大于新生代所有对象总空间, 如果大于, 说明 Minor GC 是安全的, 否则会判断是否允许担保失败, 如果允许, 判断老年代最大连续可用空间是否大于历次晋升到老年代的对象的平均大小, 如果大于, 则执行 Minor GC, 否则执行 Full GC.
当在 java 代码里直接调用 System.gc() 时, 会建议 JVM 进行 Full GC, 但一般情况下都会触发 Full GC, 一般不建议使用, 尽量让虚拟机自己管理 GC 的策略.
永久代 (方法区) 中用于存放类信息, jdk1.6 及之前的版本永久代中还存储常量, 静态变量等, 当永久代的空间不足时, 也会触发 Full GC, 如果经过 Full GC 还无法满足永久代存放新数据的需求, 就会抛出永久代的内存溢出异常.
大对象 (需要大量连续内存的对象) 例如很长的数组, 会直接进入老年代, 如果老年代没有足够的连续大空间来存放, 则会进行 Full GC.
最后
后续会持续更新 JVM 专题知识及更多架构专题, 写的不好的地方也希望大牛能指点一下, 大家觉得不错可以点个赞在关注下我, 刚刚入驻, 以后还会分享更多文章!
来源: http://www.jianshu.com/p/3dc1a2e53d93