GC
- 1960年,Lisp使用GC
- Java中,GC的对象是堆空间和永久区
GC算法
引用计数法(没有被Java采用)
- 为每个对象标记引用数量,引用数量为0时进行空间释放。
引用和去引用伴随加法和减法,影响性能;
很难处理循环引用。
标记清除(Java老年代)
- 标记清除算法是现代垃圾回收算法的基础。分为标记和清除两个阶段。
- 在标记阶段,通过根节点标记所有从根节点开始的可达对象。未被标记的对象时未被引用的垃圾对象。然后再清除阶段清除所有未被标记的对象。
标记压缩(Java老年代)
- 适用于存活对象比较多的场合,如老年代。
- 标记压缩算法也是在标记阶段,从根节点开始的可达对象。但之后,它并不简单的清理未标记对象,而是将所有存活对象压缩到内存的一端,之后,清理边界外所有的空间。
复制算法(Java新生代)
- 与标记清除算法相比,复制算法是一种相对高效的回收方法。
- 不适合存活对象较多的场合,如老年代。
- 将原有的内存空间分成两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色。
两块空间完全相同,每次只用一块;
空间浪费问题。
分代思想
- 根据对象存活周期进行分类,短命对象归为新生代,长命对象归为老年代。
- 根据不同代的特点,选择不同的算法
少量对象存活,适合复制算法;
大量对象存活,适合标记清理或标记压缩算法。
可触及性
- 可触及对象
根节点的引用链可以触及到的对象。 - 可复活的
一旦所有引用被释放,就是可复活状态,在finalize()中可能复活该对象。 - 不可触及的
在finalize()之后,可能会进入不可触及状态,进入该状态后不可被复活,可以被回收。
package cn.chen.gc;
public class CanReliveObj {
public static CanReliveObj obj;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("CanReliveObj finalize called");
obj = this;
}
@Override
public String toString() {
return super.toString();
}
}
class Main{
public static void main(String[] args) throws InterruptedException {
CanReliveObj.obj = new CanReliveObj();
CanReliveObj.obj = null;
System.gc();
Thread.sleep(1000);
if(CanReliveObj.obj == null)
System.out.println("obj是null");
else
System.out.println("obj可用");
System.out.println("第二次gc");
CanReliveObj.obj = null;
System.gc();
Thread.sleep(1000);
if(CanReliveObj.obj == null)
System.out.println("obj是null");
else
System.out.println("obj可用");
}
}
CanReliveObj finalize called
obj可用
第二次gc
obj是null
Process finished with exit code 0
避免使用finlize(),操作不慎可能导致错误;
优先级低,何时被调用不确定(何时发生GC不确定);
释放资源可以用try-catch-finally来代替它。
根
- 在栈中引用的对象是根;
- 方法区中静态成员或者常量引用的对象(全局对象);
- JNI方法栈中的对象。
Stop-The-World
- Java中的一种全局暂停现象。
- 全局暂停,所有Java代码停止,native方法可以执行,但不能与JVM交互。
- Dump线程、死锁检查、堆Dump也可能会导致STW(多半由GC引起)。