垃圾收集器

来源:互联网 发布:迅雷mac会员破解版 编辑:程序博客网 时间:2024/04/29 09:03
(一)判断对象存活方法
1、引用计数算法:
    给对象中添加一个引用计数器,当有地方引用它,计数器值加1,;当引用失效,计数器值减1,任何计数器值为0的对象就是不可能再被使用的垃圾对象。
    引用计数算法实现简单,判断效率高,但Java没有选用,主要是因为它很难解决对象之间相互循环引用的问题。
2、根搜索算法:
    基本思路是通过一系列名为“GC Roots”的对象为起点,从这些节点开始向下搜索,走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连时,证明此对象时不可用的。
    作为GC Roots的对象包括:虚拟机栈中的引用对象、方法区中的静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI的引用的对象。
    对象判断死亡后,处于“缓刑阶段”,至少经历两次标记过程,如果对象被标识,会进行选择,判断此对象是否必要执行finalize()方法,如果判断必要执行,会放置到F-Queue队列中,然后建立低优先级线程执行,但不承诺会执行结束,如果对象在finalize方法重新关联到引用链,可以逃脱死亡,否则在第二次标志将被移出执行回收。finalize方法不保证一定可以执行,而且永远只可以执行一次。

(二)引用的定义
JDK1.2之前,如果reference类型的数据中存储的数值代表另一块内存的起始地址,就称为这块内存代表一个引用。这种定义很纯粹,但太过狭义。
JDK1.2之后,引用分为四种:
1、强引用,就是指程序代码中普遍存在的,只要强引用存在,垃圾收集器永远不会回收该对象。
2、软引用:用了描述一些还有用,但非必需的对象,在将要发生内存溢出时,会把这些对象列进回收范围,如果回收掉还没有足够内存才抛出内存溢出异常。
3、弱引用:也是描述非必需对象,强度比软引用更弱一点,相关联的对象只能生存岛下一次垃圾收集之前,到垃圾收集器工作,无论内存是否足够,都会回收。
4、虚引用:最弱 的一种引用关系,一个对象是否有虚引用,不会对生存时间产生影响,无法通过虚引用取得对象,设置虚引用唯一目的是希望回收对象时收到一个系统通知。

(三)垃圾回收算法
1、标志-清除算法:
    如它名字一样,算法分为“标志”和“清除”两个阶段。首先对需要回收对象进行标记,标记完后统一回收掉所有标志的对象。
    主要缺点:一是效率问题,标记和清除过程效率不高;二是空间问题,标记清除产生大量不连续的内存碎片。太多碎片导致后面过程大对象无法找到足够连续内存,不得不提前触发另一次垃圾收集。
2、复制算法:
    将内存分为大小相同两块,当一块内存用完,将活着的对象复制到另一块上面,然后一次清理掉使用过的内存空间。这样可以每次对一块进行回收,不用考虑内存碎片问题,只需一点堆顶指针,按顺序分配,实现简单,运行高效。但代价是把内存缩小为原来的一半,代价太高了点。
    现在商业虚拟机都采用这种收集算法,据研究表明,新生代的对象98%是朝生夕死得,所以不是按1:1比例划分空间,而是将内存分为一块比较大的Eden空间和两块Survivor空间,每次使用Eden和一块Survivor空间。回收时将Eden和Survivor中存活对象拷贝到另一块中,再一次清理掉Eden和刚用过的Survivor空间。Eden空间和Survivor空间比例是8:1,当Survivor空间不够用时,需要依赖其他内存(老年代)进行担保。
3、标记-整理算法:
    复制算法在对象存活率较高时执行较多的复制操作,效率会变低,所以在老年代一般不能直接选用这种算法。
    根据老年代特点,提出与标记-清除算法一样标记,但后续步骤不是直接回收对象,而是让存活对象向一端移动,然后直接清理端边界以外的内存。
4、分代收集算法:
    当前商业虚拟机的垃圾收集都采用“分代收集”,就是根据对象存活周期的不同划分为几块,一般划分为新生代和老年代,然后根据各年代特点采用适当算法。
    新生代中每次收集有大量对象死亡,选用复制算法;老年代中存活率高,采用“标志-清理”或“标志-整理”。
原创粉丝点击