垃圾回收机制(gabage collection):针对垃圾对象的一个回收,主要发生在jvm的堆内存中,首先我们要判断这是不是个垃圾对象,然后根据不同的垃圾算法去进行回收。
一 .判断一个对象是否存活
在Java虚拟机中,判断对象是否存活有2种方法:
- 引用计数法
- 可达性分析法
1.引用计数法
在java中,是通过引用来操作对象的。假如说一个对象有引用指向它,则意味着它是存活的。给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
实现简单,效率较高,但是它无法解决循环引用的问题。
2.可达性分析法
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。
可作为 GC Root 的对象有:
- JVM虚拟机栈中的引用的对象(当前栈帧的本地变量表引用对象)。
- 方法区中静态引用 / 常量引用指向的对象。
- 本地方法栈中JNI引用的对象。
二.垃圾回收算法
1.标记清除算法(mark-sweep)
标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。
实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
适用于:对象存活率较低 & 垃圾回收行为频率低 的场景
2.复制算法(coping)
将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
复制(Copying)算法实现简单,运行高效且不容易产生内存碎片,但是能够使用的内存缩减到原来的一半。Copying算法的效率跟存活对象数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将大大降低。
适用于:对象存活率较低 & 需要频繁进行垃圾回收 的区域
3.标记整理算法(mark-compact)
该算法标记阶段和标记-清除(Mark-Sweep)一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
适用于:对象存活率较低 & 垃圾回收行为频率低 的场景
4.逐代回收算法
- 新建的对象 一般会被优先分配到新生代的Eden区、From Survivor区
- 这些对象经过第一次 Minor GC后,若仍然存活,将会被移到To Survivor区
- 在 To Survivor 区每经过一轮 Minor GC ,该对象的年龄就+1
- 当对象年龄达到一定时(阈值默认=15),就会被移动到老年代。
效率高、空间利用率高,根据不同区域特点 选择 不同的垃圾收集算法。新生代区域:采用复制算法;老年代区域:采用标记-清除算法、标记 - 整理算法