前面在jvm组成结构一文中,说到了GC和一些算法,那么在这篇文章里,就详细说说GC的常用算法。
垃圾回收(Garbage Collection,GC),就是将垃圾回收,避免过于占用内存空间,导致内存泄漏,对内存堆中已经死亡或者长时间没有使用的对象进行清除和回收。
既然是垃圾回收,那么如何判断哪些对象是垃圾,需要被回收呢?

如何找到程序里的垃圾

(1)引用计数法


引用计数法其实就是给每个对象添加一个计数器RC,当有地方引用该对象时计数器加1,当引用失效时计数器减1。通过对象计数器是否为0来判断对象是否可被回收。但是相应的,它无法解决循环引用的问题。
假使如下:
定义一个字符串

String str = new String ("tony");

其实这个时候,str引用了字符串"tony",那么这个字符串引用次数就是1,这时候如果m指向了另一个新的值,例如 m=null,那这个时候"tony"的引用次数就成了0了,也就意味着要被回收了。

public class ReferenceCountingGC {

  public Object instance;

  public ReferenceCountingGC(String name) {
  }
  public static void testGC(){

    ReferenceCountingGC a = new ReferenceCountingGC("objA");
    ReferenceCountingGC b = new ReferenceCountingGC("objB");

    // a和b互相引用了
    a.instance = b;
    b.instance = a;

    a = null;
    b = null;
  }
}

从代码中可以看出,a,b这两个对象一直在互相引用,如果采用引用计数法的话,是永远无法GC它们的,因为它们的引用计数永远都不会为0,所以说引用计数法是有局限性的,或者说它不属于严格意义上的垃圾收集机制。

(1)可达性分析算法


GC ROOT是什么?

  • 首先我们知道标记算法,JVM的标记算法我们可以了解为一个可达性算法,所以所有的可达性算法都会有起点,那么这个起点就是GC Root。也就是需要通过GC Root 找出所有活的对象,那么剩下所有的没有标记的对象就是需要回收的对象。
  1. 通过 GC ROOT的对象作为搜索起始点,通过引用向下搜索,所走过的路径称为引用链。通过对象是否有到达引用链的路径来判断对象是否可被回收(可作为GC ROOT的对象:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象)
  2. 通过可达性算法,成功解决了引用计数所无法解决的循环依赖问题,只要你无法与GC Root建立直接或间接的连接,系统就会判定你为可回收对象。那这样就引申出了另一个问题,哪些属于GC Root
  3. Java内存区域中哪些可以作为GC Root对象?
虚拟机栈中引用的对象
public class StackLocalParameter {

  public StackLocalParameter(String name) {}

  public static void testGC() {
    StackLocalParameter s = new StackLocalParameter("localParameter");
    s = null;
  }
}
方法区中类静态属性引用的对象
public class MethodAreaStaicProperties {

  public static MethodAreaStaicProperties m;

  public MethodAreaStaicProperties(String name) {}

  public static void testGC(){
    MethodAreaStaicProperties s = new MethodAreaStaicProperties("properties");
    s.m = new MethodAreaStaicProperties("parameter");
    s = null;
  }
}
方法区中常量引用的对象
public class MethodAreaStaicProperties {

  public static final MethodAreaStaicProperties m = MethodAreaStaicProperties("final");

  public MethodAreaStaicProperties(String name) {}

  public static void testGC() {
    MethodAreaStaicProperties s = new MethodAreaStaicProperties("staticProperties");
    s = null;
  }
}
本地方法栈中引用的对象
image.png

任何native接口都会使用某种本地方法栈,实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。
image.png


玛卡巴卡
1 声望2 粉丝

后端开发