前面在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 找出所有活的对象,那么剩下所有的没有标记的对象就是需要回收的对象。
- 通过
GC ROOT
的对象作为搜索起始点,通过引用向下搜索,所走过的路径称为引用链。通过对象是否有到达引用链的路径来判断对象是否可被回收(可作为GC ROOT的对象:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI引用的对象) - 通过可达性算法,成功解决了引用计数所无法解决的循环依赖问题,只要你无法与
GC Root
建立直接或间接的连接,系统就会判定你为可回收对象。那这样就引申出了另一个问题,哪些属于GC Root
。 - 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;
}
}
本地方法栈中引用的对象
任何native接口都会使用某种本地方法栈,实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。