垃圾回收(Garbage Collection, GC)是Java不同于c与c++的重要特性之一。
他帮助Java自动清空堆中不再使用的对象。
由于不需要手动释放内存,程序员在编程中也可以减少犯错的机会。
利用垃圾回收,程序员可以避免一些指针和内存泄露相关的bug(这一类bug通常很隐蔽)。
垃圾回收实际上是将原本属于程序员的责任转移给计算机。
GC需要完成的3件事情:
哪些内存需要回收
什么时候回收
如何回收
1 回收那些对象?
在Java中采用可达性分析算法来判定对象是否存活,是否可以被回收。
这个算法通过一系列的被称为”GC Root”的对象作为根节点,
从他们开始向下搜索,搜索走过的路径被称为引用链(Reference Chain).
当一个对象没有一条引用链与GC Root 连接时,
即从GC Root 到这个对象是不可达的,说明这个对象是不可用的。
如图示:
object5 object6 object7 虽然互联互通
但是他们到GC Root是不可达的
所以他们将被判定为可以回收的对象
那么有一个重要的问题是 what is GC Root?
在Java语言中,可以看做是GC Root的是:
虚拟机栈中引用的变量 (可理解为方法中的局部变量)
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中的JNI(native方法)引用的对象
2 垃圾回收算法
2.1 标记-清除算法
顾名思义,该方法分为标记和清除2个过程
标记:将所有需要回收的对象区域进行标记
清除:清除所有配标记的区域里的对象
算法不足之处:
效率问题:标记和清除的效率都不高
空间问题:清除后产生的空间是不连续的碎片
无法满足后续运行中大对象的需求
2.2 复制算法
将整个空间划分2个相等的区域,每次只使用其中一个区域
当一块内存不够时,就将活着的对象复制到另一块内存
然后将第一块的内存全部回收。
这样每次对整个半区回收,就不会有内存碎片的情况,实现简单,运行高效
问题: 该算法的代价就是可以内存大小缩小为原来的一半
解决:现在商用的虚拟机都采取复制算法.
但由于所有的对象是朝生夕死的,所以并不是按照1:1的比例来划分内存的
而是将内存划分为一块较大的Eden区(new一个对象是就是在这里面分配空间)
和2块Survivor区域。每次使用Eden和一块Survivor。
当回收的时候,将Eden和Survivor区中还存活的对象一次性赋值到另一块Survivor中
最后清理掉原来使用过的Eden和Survivor区
这3个区域也被称为新生代。HotSpot的默认新生代各区域比例如下:
每次新生代中可用的空间为整个新生代的90%, 即80% Eden + 10% 1个Survivor
此时只有10% 1个Survivor 会被’浪费’
如果另一块Survivor区域存放不下Eden和Survivor区存活下来的对象
就要依靠其他区域来存放 即老年代
2.3 标记-整理算法
类似于标记-清除算法,先标记所有可以回收的区域,然后不是直接回收,
而是把所有存活的对象都移动到一端,然后直接清理掉端边界以外的区域
标记-整理算法和标记-清除算法常用于老年代的回收
老年代存放的对象存活的时间较长
而且垃圾回收的频率不如新生代的频繁