先简单说说Java运行时内存区,划分为线程私有区和线程共享区
(1)线程私有区:
1、 程序计数器,记录正在执行的虚拟机字节码的地址;
2、拟机栈:方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧;
3、本地方法栈:虚拟机的Native方法执行的内存区;
(2)线程共享区:
1、Java堆:对象分配内存的区域,这是垃圾回收的主战场;
2、方法区:存放类信息、常量、静态变量、编译器编译后的代码等数据,另外还有一个常量池。当然垃圾回收也会在这个区域工作。
public class GcDemo {
public static void main(String[] args) {
//
分为6
个步骤
GcObject obj1 = new GcObject(); //Step1
GcObject obj2 = new GcObject(); //Step2
obj1.instance = obj2; //Step 3
obj2.instance = obj1; //Step 4
obj1 = null; //Step 5
obj2 = null; //Step 6
}
}
classGcObject{
public Object instance = null;
}
引用计数是唯一一种没有使用根集的垃圾回收算法,该算法使用引用计数器来区分存活对象和不再使用的对象。
· Step1:GcObject实例1的引用计数加1,实例1的引用计数=1;
· Step2:GcObject实例2的引用计数加1,实例2的引用计数=1;
· Step3:GcObject实例2的引用计数再加1,实例2的引用计数=2;
· Step4:GcObject实例1的引用计数再加1,实例1的引用计数=2;
执行到Step 4,则GcObject实例1和实例2的引用计数都等于2。· Step5:栈帧中obj1不再指向Java堆,GcObject实例1的引用计数减1,结果为1;
· Step6:栈帧中obj2不再指向Java堆,GcObject实例2的引用计数减1,结果为1。
到此,发现GcObject实例1和实例2的计数引用都不为0,那么如果采用的引用计数算法的话,那么这两个实例所占的内存将得不到释放,这便产生 了内存泄露。 情况(二):可达性算法
这是目前主流的虚拟机都是采用GC Roots Tracing算法,JAVA或C#语言都用的是该算法。
这个算法的基本思路就是通过一系列的称为“GC Root”的对象作为起始点,利用数学中图论知识,从这些节点开始向下搜素,搜索所走过的路径称为应用链,当一个对象到GC Roots没有任何引用链相连时。则证明此对象时不可用的,可以被回收了。如下图对象object5,object6,object7虽然互相有关系,但是没有GCroots可以达到他们。所以他们时可以被回收的对象。如下图:图中可达对象便是存活对象,而不可达对象则是需要回收的垃圾内存。
这里涉及两个概念,一是GC Roots,一是可达性。
那么,可以作为GC Roots的有:
1、虚拟机栈的栈帧的局部变量表所引用的对象;
2、本地方法栈的JNI所引用的对象;
3、方法区的静态变量和常量所引用的对象;
从上图可看见obj1与obj2都是GC roots,但因为程序的最后有obj1=null,obj2=null导致断开了连接,所以就没有
相通的了。即为不可到达对象。不可到达对象就会被回收了。
所以这个题的结论是:
GcObject实例1和实例2虽然从引用计数虽然都不为0,但从可达性算法来看,都是GC Roots不可达的对象。