上一篇介绍了Java内存运行时区域的各个部分,其中程序计数器、 虚拟机栈、 本地方法栈3个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。 每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的。。 而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是Java堆和方法区这部分内存。
对象存活判定算法
1、引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的
引用计数算法(Reference Counting)的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法。但是,至少主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。
举个简单的例子,如下面代码所示:对象objA和objB都有字段instance,赋值令objA.instance=objB及objB.instance=objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是它们因为互相引用着对方,导致它们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。
/**
*testGC()方法执行后,objA和objB会不会被GC呢?
*@author zzm
*/
public class ReferenceCountingGC{
public Object instance=null;
private static final int_1MB=1024*1024;
/**
*这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
*/
private byte[]bigSize=new byte[2*_1MB];
public static void testGC(){
ReferenceCountingGC objA=new ReferenceCountingGC();
ReferenceCountingGC objB=new ReferenceCountingGC();
objA.instance=objB;
objB.instance=objA;
objA=null;
objB=null;
//假设在这行发生GC,objA和objB是否能被回收?
System.gc();
}
}
结果如下:
[F u l l G C(S y s t e m)[T e n u r e d:0 K->2 1 0 K(1 0 2 4 0 K),0.0 1 4 9 1 4 2 s e c s]4603K->210K//(19456K),[Perm:2999K->
2999K(21248K)],0.0150007 secs][Times:user=0.01 sys=0.00,real=0.02 secs]Heap
def new generation total 9216K,used 82K[0x00000000055e0000,0x0000000005fe0000,0x0000000005*fe0000)
Eden space 8192K,1%used[0x00000000055e0000,0x00000000055f4850,0x0000000005de0000)
from space 1024K,0%used[0x0000000005de0000,0x0000000005de0000,0x0000000005ee0000)
to space 1024K,0%used[0x0000000005ee0000,0x0000000005ee0000,0x0000000005fe0000)
tenured generation total 10240K,used 210K[0x0000000005fe0000,0x00000000069e0000,0x00000000069e0000)
the space 10240K,2%used[0x0000000005fe0000,0x0000000006014a18,0x0000000006014c00,0x00000000069e0000)
compacting perm gen total 21248K,used 3016K[0x00000000069e0000,0x0000000007ea0000,0x000000000bde0000)
the space 21248K,14%used[0x00000000069e0000,0x0000000006cd2398,0x0000000006cd2400,0x0000000007ea0000)
No shared spaces configured
2、可达性分析算法
这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所
走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。 如下图所示,对象object 5、 object 6、 object 7虽然互相有关联,但是它们到GC Roots是不可达
的,所以它们将会被判定为是可回收的对象。
可作为GC Roots的对象包括下面几种:
1)虚拟机栈(栈帧中的本地变量表)中引用的对象。
2)方法区中类静态属性引用的对象。
3)方法区中常量引用的对象。
4)本地方法栈中JNI(即一般说的Native方法)引用的对象。