一、内存垃圾回收
我们都知道,在Java内存模型中,虚拟机栈、本地方法栈以及程序计数器是线程私有的。这部分内存随着线程或者方法的结束,内存自动释放。而对于线程共享的堆和方法区来说,一个接口中多个实现类的内存是不一样的,很难分辨出,位于堆内存中的哪些对象是没用的。只有在程序运行的时候,才知道创建了哪些对象。而堆内存又是JVM内存中最大的部分,必须对这部分进行内存管理,否则就会造成内存溢出。
因此,这部分内存的分配和回收都是动态的,所以垃圾回收器关注的就是这部分内容。
二、判断对象存活的算法
垃圾回收器在回收堆内存时,需要先判断哪些对象存活,哪些对象死亡(指堆中的对象没有被任何变量或常量引用)。
1.引用计数算法
顾名思义,就给在创建对象的时候,额外给每个对象加一个引用计数器,当该对象被引用时,这个引用计数器就+1;当引用取消时,引用计数器就-1。但是JVM并没有采用这种算法来判断对象是否死亡,因为这中算法并没有解决对象之间循环互相引用的问题。
public class QuoteNum {
private QuoteNum instanse = null;
private static final int _1MB = 1024*1024;
// 为了占点内存 看垃圾回收效果
private byte[] bigSize = new byte[2 * _1MB];
public static void main(String[] args) {
QuoteNum q1 = new QuoteNum();
QuoteNum q2 = new QuoteNum();
// 互相引用
q1.instanse = q2;
q2.instanse = q1;
// 取消q1 q2 的引用 但此时引用计数器并不为0
q1 = null;
q2 = null;
// 此时再也无法访问到这两个对象
// 提醒JVM回收
System.gc();
}
}
如果采用引用计数算法,那么两个对象是无法进行回收的,通过设置JVM参数,查看其GC日志发现,这两个对象已经被回收。可以得出,JVM判断对象是否死亡并没有采用引用计数算法。
运行结果:
2. 可达性分析算法
实际上JVM判断对象是否可以被回收采取的是可达性分析算法。基本思路就是通过一系列称为GC-Roots的对象作为起始点,通过这些点向下搜索,形成一系列的引用链。如果一个对象到GC-Roots不存在引用链,就说明这个对象需要被垃圾回收。
(1) 内存回收第一步
在Java语言中可作为GC-Roots的对象包括以下几种
- 虚拟机栈中本地变量表引用的对象
- 方法区中类静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈中引用的对象
如图所示:对象4、5、6、没有到达GC-Roots的引用链,就有可能别垃圾回收器回收。
(2) 内存回收第二步
即使对象不可达GC-Roots,也不能确定该对象所占的内存必须被回收,要真正宣告一个对象的死亡,必须经历两次标记的过程。第一次为不可达分析算法中的标记,第一次标记后,将会进行第二次筛选,筛选的条件是否需要执行finalize()方法。如果需要执行则加入F-queue中,由虚拟机创建一个线程执行这个方法。
对象逃脱死亡的最后依次机会,在执行该方法时,对象可以将自己的赋值给某个类变量或者对象的成员变量。否则进行第二次标记,等待垃圾回收器回收。
演示代码:
public class GCtest {
public static GCtest gCtest = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("执行finalize方法");
GCtest.gCtest = this;
}
public void alive(){
System.out.println("我还活着");
}
public static void main(String[] args) throws InterruptedException {
gCtest = new GCtest();
gCtest = null;
System.gc();
Thread.sleep(500);
if(gCtest != null)
gCtest.alive();
else
System.out.println("垃圾回收器已经回收GCTest对象");
// 因为一个对象的finalize()方法只能执行依次 所以这次被回收了
gCtest = null;
System.gc();
Thread.sleep(500);
if(gCtest != null)
gCtest.alive();
else
System.out.println("垃圾回收器已经回收GCTest对象");
}
}
运行结果: