码迷,mamicode.com
首页 > 其他好文 > 详细

深入理解JVM:垃圾收集器与内存分配策略

时间:2015-08-18 21:30:33      阅读:231      评论:0      收藏:0      [点我收藏+]

标签:jvm   内存分配   算法   垃圾收集器   

堆里面存放着Java世界几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还存活,哪些已经死去。判断对象的生命周期是否结束有以下几种方法
引用计数法
具体操作是给对象添加一个引用计数器,每当有一个地方引用时,计数器的值就加1,;当引用失效时,计数器就减1;任何时刻计数器为0的对象就 是不可能再被使用的。客观的说引用计数器算法实现简单,判定效率也很高,在大部分情况下他都是一个不错的算法。但是引用计数器有缺陷
举个简单的例子,对象A和对象B都有字段instance,赋值命令objA.instance = objB,objB.instance=objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问了,但是他们因为互相引用这对方,导致引用数据器不为0,无法通知CG收集器收集他们。

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;

        obj.A = null;
        obj.B = null;
    }
}

通过GC日志,虚拟机并没有因为这两个对象互相引用就不回收他们,这从侧面说明虚拟机不是通过引用计数器来判断对象是否存活。
可达性分析算法
这个算法的基本思路是通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可用的。
Java语言中,可作为GC Roots的对象包括
1、虚拟机栈(栈帧中的本地变量表)中引用的变量
2、方法区中类静态属性引用的对象
3、方法区中常量引用的对象
4、本地方法栈中JNI(Native方法)引用的对象
引用分析
如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称为这块内存代表着一个引用。这种定义太过狭隘,一个对象在这种定义下只有被引用和没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象,当内存空间足够时,则能够保存在内存之中,如果内存空间在进行垃圾回收后还是非常紧张,则可以抛弃这些对象。
在JDK1.2以后,Java对引用进行了扩展,将引用分为强引用、软引用、弱引用、虚引用四种,强度依次递减。
1、强引用:程序中普遍存在,例如Object obj = new Object();只要强引用来,CG永远不会回收
2、软引用:描述一些还有用,但是并非必要的对象。对于软引用的对象,在系统将要发生内存溢出之前,会把这些对象列进回收范围之中进行二次回收,如果回收了还没有足够的内存将报出内存溢出,JDK1.2之后提供了SoftReference类来实现软引用。
3、弱引用:也是描述非必须对象,被弱引用的对象只能生存到下一次垃圾收集之前。当垃圾收集器工作时,无论内存是否够用都会被回收,JDK1.2之后提供了WeakReference类来实现弱引用。
4、虚引用:幽灵引用或者幻影引用,最弱。一个对象是否有虚无引用的存在,完全不会对其生存时间构成影响,也无法通过虚无引用来取得一个对象实例。
生存还是死亡
即使在可达性分析算法中不可达对象,也并非是“非死不可”的,这时候他们暂时出“缓刑”之中,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那他会被第一次标记并且进行一次筛查,筛查的条件是此对象是有有必要执行finalize方法。当对象没有覆盖finalize方法,或者finalize方法已经被虚拟机调用过,虚拟机将这两种情况视为没有必要执行。
类的Finalize方法,可以告诉垃圾回收器应该执行的操作,该方法从Object类继承而来。在从堆中永久删除对象之前,垃圾回收器调用该对象的Finalize方法。注意,无法确切地保证垃圾回收器何时调用该方法,也无法保证调用不同对象的方法的顺序。即使一个对象包含另一个对象的引用,或者在释放一个对象很久以前就释放了另一个对象,也可能会以任意的顺序调用这两个对象的Finalize方法。如果必须保证采用特定的顺序,则必须提供自己的特有清理方法。
回收方法区
方法区即为Hotspot虚拟机中的永久代,永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。以常量字符串”abc”为例,已经进入常量池,但是当前系统没有任何一个String对象叫做abc,换句话说就是没有任何String对象引用常量池中的abc,如果此时发生内存回收,这个abc常量就会被系统清理出常量池。常量池中的其他类、接口、方法、字段的符号引用也与此类似。
判定一个常量是否是废弃常量比较简单,而判定一个类是否是无用的类的条件 要苛刻的多,无用的类要满足以下条件:
1、该类所有的实例都已经被回收,Java堆中不存在该类的任何实例。
2、加载该类的ClassLoader已经被回收
3、该类的Java.lang.class对象没有在任何地方被引用,无法在任何地方通过反射访问该类。

深入理解JVM:垃圾收集器与内存分配策略

标签:jvm   内存分配   算法   垃圾收集器   

原文地址:http://blog.csdn.net/fuyuwei2015/article/details/47758047

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!