天天看点

[jjzhu学java]之深入理解JVM之垃圾收集器与内存分配策略深入理解JVM之垃圾收集器与内存分配策略

<a href="#%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3jvm%e4%b9%8b%e5%9e%83%e5%9c%be%e6%94%b6%e9%9b%86%e5%99%a8%e4%b8%8e%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e7%ad%96%e7%95%a5">深入理解jvm之垃圾收集器与内存分配策略</a>

<a href="#%e5%a6%82%e4%bd%95%e5%88%a4%e6%96%ad%e5%af%b9%e8%b1%a1%e5%b7%b2%e7%bb%8f%e6%b6%88%e4%ba%a1">如何判断对象已经消亡</a>

<a href="#%e5%bc%95%e7%94%a8%e8%ae%a1%e6%95%b0%e7%ae%97%e6%b3%95">引用计数算法</a>

<a href="#%e6%a0%b9%e6%90%9c%e7%b4%a2%e7%ae%97%e6%b3%95">根搜索算法</a>

<a href="#%e5%bc%95%e7%94%a8">引用</a>

java中对象的创建需要的内存都是在java堆中申请的,所以垃圾收集的区域就是对java堆和方法区的内存区域进行gc。

垃圾收集器的主要任务就是找出已经“消亡”的对象,将其标记并清除其说用内存的过程,如何判断某个对象已经“消亡”,不同的虚拟机有不同的判断策略

引用计数(reference counting)算法的基本思想就是:给每个对象添加一个引用计数器,每当有一个地方对该对象进行了引用,引用计数器就加1;引用失效后就减1;当引用计数器为0时,就表示没有任何地方引用了该对象,这可以认为该对象已经“消亡”了。

虽然引用计数算法思想简单,效率也很高,但是java虚拟机并没有用到该算法标记“消亡”对象,因为当出现循环引用的时候,表现就不是那么好了。我们可以编写测试代码测试并看gc信息看看java虚拟机到底有没有用该算法。

代码示例中,新建了两个对象obja,objb,然后通过obja.instance = objb,objb.instance = obja让他们互相引用,然后将obja、objb都置为空,将会触发gc,我们可以通过-xx:+printgcdetails让java虚拟机在发生gc后打印出gc的具体信息,

代码片运行时的vm参数为-xmx20m -xms20m -xx:+printgcdetails

运行程序,可看到如下打印结果:

在分析结果前,先对gc的内容先做一个介绍:

1、gc日志的第一行[gc [psyounggen说明了在新生代发生了gc(minor gc),这里也可以看出,当前的hotspot虚拟机采用的是parallel scavenge(ps)垃圾收集器 后面的4340k-&gt;256k代表gc前后的新生代内存区域的变化,这里从4340k变到256k,说明虚拟机进行了垃圾回收,后面的(5952k)代表新生代的内存区域大小,4340k-&gt;256k(19648k)这里的括号内的19648k代表新生代和年老代的内存大小。之后的0.0009373 secs代表的是gc所用的事件。 2、gc日志的第二行[full gc (system)表示系统触发的一次full gc,也就是代码中system.gc();所引起的gc,一次full gc会对java堆中的所有区域进行gc(新生代、年老代、永久代),所以后面的psyounggen(新生代)、psoldgen(年老代)、pspermgen(永久带)显示了各区域的gc情况,我们可以看到psyounggen: 256k-&gt;0k(5952k),新生代经过full gc后,全被清空了。 3、后面的heap堆显示了各区域的最终使用情况 从最后的full gc (system) [psyounggen: 256k-&gt;0k(5952k)]可以看到,新生代中的内存全被gc了,所以说,hotspot并没有用引用计数算法来对“消亡”对象进行gc。

现在一般的垃圾收集器都是用该算法(gc root tracing)来判断对象是否“消亡”,该算法的基本思想就是:通过一组称为“gc roots”的对象作为根节点,然后从这些根节点向下搜索,搜索所走过的路径称为引用链(reference chain),当一个对象到根节点之间没有任何一条引用链的话,就认为该对象已经“消亡”,如下图所示:

可以作为gc roots的对象包括:

1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象

2. 方法区中 的类静态属性引用的对象

3. 方法区中的常量引用的对象

4. native方法引用的对象

任何判断对象是否消亡都是通过引用来判断,引用以前的定义是:如果reference类型的数据中存储的数值代表的是另一块内存区域的起始地址的话,就称这块内存代表一个引用