标签:
此文内容摘自<深入理解java虚拟机 第二版>,选择一些脉络性的记录下,自己的理解很少,有时间看看还是可以的。
在堆里存放这java世界几乎所有的对象实例,垃圾回收器在对堆进行回收前,第一件事就是要确定这些对象之中那些对象还“存活”着,
哪些对象已经“死去”(即不可能再被任何途径使用的对象)。
给对象添加一个引用计数器,每当有一个地方引用它时,计数器值加1;当引用失效时就减1;任何计数器为0的对象就是不可能再被使用的。
引用计数器法实现简单,判断效率高,在大部分情况下是不错的算法。但是在主流的java虚拟机里面没有选用引用计数器法来管理内存,主要的
原因是它很难解决对象之间的相互循环引用的问题。如这种代码:
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
System.gc();//假如在这里发生gc,objA和objB实际上并没有回收。
算法的基本思路是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots
没有任何引用链相连时(从GC Roots到这个对象不可达),则证明此对象是不可用的。
在java语言中,可以作为GC Roots的对象包括以下几种:
(1) 虚拟机栈(栈帧中的本地变量表)中引用到的对象;
(2) 方法区中类静态变量引用到的对象;
(3) 方法去中常量引用到的对象;
(4) 本地方法栈中JNI (即一般所说的Native方法) 引用到的对象。
如下图所示,object5、object6、object7虽然有关联,但是它们到GC Roots是不可达的,会被判定为可回收对象。
在JDK1.2之前,java中的引用很传统:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。
一个对象在这种定义下只有引用或者没有没引用两种状态。
在JDK1.2之后,java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用 4种。
最基础的收集算法是 标记 - 清除 算法算法分为 标记 和清除两个阶段:首先标记出所有需要被回收的对象,在标记完成后统一回收所有被标记的对象。
不足:一个是效率问题,标记和清除两个过程效率都不高;另一个是空间问题,标记清除后产生大量的不连续的内存碎片,空间碎片太多可能导致以后
在需要分配较大对象时,无法找到足够的了连续内存而不得不提前触发另一次垃圾回收动作。
下图是 标记 - 清除 算法的执行过程图:图中上半部分是回收前状态,下半部分是回收后状态。
(下面这三张图都是直接拍的相片)
接了解决效率问题,一种称为“f复制”的算法出现了,它将可用内存分为大小相等的两块,每次只是用其中一块。当着一块的内存用完了,就将还存活的
对象复制到另一块上面,然后把使用过的内存一次清理掉。这样每次都是对整个半区进行内存回收,内存分配时不用考虑内存碎片等复杂情况,只要一动栈顶指针,
按顺序分配内存即可,实现简单,运行高效。
不足:将内存缩小为原来的一般,代价有点高。
下图是复制算法示意图:(此图中 存活对象 和保留区域 颜色基本一样,自己区分)
根据老年代的特点(对象存活率比较高),有人提出了另外一种 “标记 - 整理” 算法,标记过程任然与“标记 - 清除” 算法一样,但后续步骤不是直接对可回收对象
进行清理,而是让所有的存活对象都向一端移动,然后直接清理掉端边界以外的内存.
下图是标记-整理算法示意图:
当前的商业虚拟机的垃圾收集器都采用 “分代收集” 算法,这种算法并没有什么新的思路,只是根据对象存活周期的不同将内存划分为几块。
一般把java堆分为新生代和老年代,这样就可以根据各个年代放入特点采用 适当的收集算法。在新生代垃圾收集时每次都发现有大批对象死去,
只有少量存活,那就选用复制算法,只要付出少量存活的对象的 复制成本就可已完成收集。而老年代中对象存活率比较高,
没有额外的空间对他进行分配担保,就必须使用“标记 - 清理” 或者 “标记 - 整理” 算法来进行回收。
3 HotSopt的算法实现
3.1 枚举根节点
3.2 安全点
3.3 安全区域
4 垃圾收集器
4.1
标签:
原文地址:http://blog.csdn.net/wufengui1315/article/details/45489115