美文网首页
Java的对象引用类型

Java的对象引用类型

作者: Hey_Shaw | 来源:发表于2018-06-19 14:19 被阅读5次
    强制系统垃圾回收的两种方式:

    调用System类的gc()静态方法:System.gc()。
    调用Runtime对象的gc()实例方法:Runtime.getRuntime.gc()。

    强制垃圾回收机制调用可恢复对象的finalize()方法以及通知系统进行资源清理

    调用System类的runFinalization()静态方法:System.runFinalization()。
    调用Runtime对象的runFinalization()实例方法:Runtime.getRuntime.runFinalization()。

    Java对对象的4中引用方式

    1、强引用(StrongReference)

    StrongReference是Java程序中最常见的引用方式。程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象,当一个对象被一个或一个以上的引用变量所引用时,它处于可达状态,不可能被系统垃圾回收机制回收。

    @Test  
    public void strongReference() {  
        Object referent = new Object();  
          
        /** 
         * 通过赋值创建 StrongReference  
         */  
        Object strongReference = referent;  
          
        assertSame(referent, strongReference);  
          
        referent = null;  
        System.gc();  
          
        /** 
         * StrongReference 在 GC 后不会被回收 
         */  
        assertNotNull(strongReference);  
    } 
    

    2、软引用(SoftReference,对应JDK中java.lang.ref.SoftReference)

    软引用需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象;当系统内存空间不足时,系统可能会回收它。软引用通常用于对内存敏感的程序中。

    @Test  
    public void softReference() {  
        Object referent = new Object();  
        SoftReference<Object> softRerference = new SoftReference<Object>(referent);  
      
        assertNotNull(softRerference.get());  
          
        referent = null;  
        System.gc();  
          
        /** 
         *  soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用 
         */  
        assertNotNull(softRerference.get());  
    }  
    

    3、WeakReference(弱引用,对应JDK中java.lang.ref.WeakReference)

    弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统回收机制运行时,不管系统内存是否足够,总会回收该对象占用的内存。

    @Test  
    public void weakReference() {  
        Object referent = new Object();  
        WeakReference<Object> weakRerference = new WeakReference<Object>(referent);  
      
        assertSame(referent, weakRerference.get());  
          
        referent = null;  
        System.gc();  
          
        /** 
         * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收 
         */  
        assertNull(weakRerference.get());  
    }  
    

    4、WeakHashMap实现类

    WeakHashMap与HashMap的用法基本相似。与HashMap的区别在于,HashMap的key保留了对实际的强引用,意味着该HashMap不会被销毁,其所有key所引用得对象就不会被回收,HashMap也不会自动删除这些key所对应的key-value对;但WeakHashMap的key只保留实际对象的弱引用,这些key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key对应的key-value对。

    public class WeakHashMapTest{
        public static void main(){
            WeakHashMap whm = new WeakHashMap();
            // 将WeakHashMap中添加三个key-value对,
            // 三个key都是匿名字符串对象(没有其他引用)
            whm.put(new String("语文") , new String("良好"));
            whm.put(new String("数学") , new String("及格"));
            whm.put(new String("英文") , new String("中等"));
            //将 WeakHashMap中添加一个key-value对,
            // 该key是一个系统缓存的字符串对象。
            whm.put("java" , new String("中等"));    // ①
            // 输出whm对象,将看到4个key-value对。
            System.out.println(whm);
            // 通知系统立即进行垃圾回收
            System.gc();
            System.runFinalization();
            // 通常情况下,将只看到一个key-value对。
            System.out.println(whm);
        }
    }
    

    编译、运行上面程序,看到如下运行如果:

    {英文=中等, java=中等, 数学=及格, 语文=良好}
    {java=中等}
    

    当系统进行垃圾回收时,删除了WeakHashMap对象的前三个key-value对。这是因为添加前三个key-value对(粗体字部分)时,这三个key都是匿名的字符串对象,WeakHashMap只保留了对它们的弱引用,这个垃圾回收时会自动删除这三个key-value对。
    WeakHashMap对象中第4个组key-value对的key是一个字符串直接量,(系统会自动保留对该字符串对象的强引用),所以垃圾回收是不会回收它。

    注:如果需要使用WeakHashMap的key来保留对象的弱引用,则不要让该key所引用的对象具有任何强引用,否则将失去使用WeakHashMap的意义。

    5、PhantomReference (虚引用,对于JDK中java.lang.ref.PhantomReference )

    通过PhantomReference类实现,徐引用类似完全没有引用。虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时,它和没有引用的效果大致相同。因为它的 get() 方法永远返回 null。

    @Test  
    public void phantomReferenceAlwaysNull() {  
        Object referent = new Object();  
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());  
          
        /** 
         * phantom reference 的 get 方法永远返回 null  
         */  
        assertNull(phantomReference.get());  
    }
    

    注:SoftReference、WeakReference以及PhantomReference均包含一个get()方法,用于获取被它们所引用的对象。

    6、ReferenceQueue(引用队列,对于JDK中java.lang.ref.ReferenceQueue)

    用于保存被回收后对象的引用。当联合使用软引用、弱引用和引用队列时,系统在回收被引用的对象之后,将把被回收对象对应的引用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象释放之前,将把它对应的虚引用添加到它关联的引用队列中,这使得可以在对象在被回收之前采取行动。程序可以检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解虚引用所引用的对象是否即将被回收。

    public class PhantomReferenceTest
    {
        public static void main(String[] args)
            throws Exception
        {
            // 创建一个字符串对象
            String str = new String("疯狂Java讲义");
            // 创建一个引用队列
            ReferenceQueue rq = new ReferenceQueue();
            // 创建一个虚引用,让此虚引用引用到"疯狂Java讲义"字符串
            PhantomReference pr = new PhantomReference (str , rq);
            // 切断str引用和"疯狂Java讲义"字符串之间的引用
            str = null;
            // 取出虚引用所引用的对象,并不能通过虚引用获取被引用的对象,所以此处输出null
            System.out.println(pr.get());  //  ①
            // 强制垃圾回收
            System.gc();
            System.runFinalization();
            // 垃圾回收之后,虚引用将被放入引用队列中
            // 取出引用队列中最先进入队列中的引用与pr进行比较
            System.out.println(rq.poll() == pr);   // ②
        }
    }
    

    因为系统无法通过虚引用获得被引用的对象,多以执行①处的输出语句时,程序输出null。当程序强制垃圾回收回收后,只有虚引用引用的字符串对象将被垃圾回收,当被引用的对象被回收后,对应的虚引用将被添加到关联的引用队列中,因而将在②代码处看到输出true。

    由于垃圾回收的不确定性,当程序希望从软、弱引用中获取被引用对象时,可能这个对象已经被释放了。如果程序需要使用被引用对象,则必须被重新创建该对象。这个过程可以采用两种方式:

    // 取出弱引用所引用的对象
    obj = wr.get();
    // 如果取出的对象为null
    if(obj == null){
        // 重新创建一个新的对象,再次让弱引用去引用该对象
        wr = new WeakReference(recreateIt()) ;
        // 取出弱引用所引用的对象,将其赋给obj变量
        obj = wr.get() ;
        ... // 操作obj对象
        // 再次切断obj和对象之间的关联
    }
    // 再次切断obj和对象之间的关联
    obj = null ;
    

    recreateIt()表示创建一个obj对象,这段代码存在一个问题,就是垃圾回收的不确定性,可能会导致新创建的弱引用再次被回收,从而获取到的obj仍然是null。另一种取出方式避免了这个问题:

    // 取出弱引用所引用的对象
    obj = wr.get();
    // 如果取出的对象为null
    if(obj == null){
        // 重新创建一个新的对象,并使用强引用来引用它
        obj = recreateIt() ;
        // 取出弱引用所引用的对象,将其赋给obj变量
        wr = new WeakReference(obj) ;
        ... // 操作obj对象
        // 再次切断obj和对象之间的关联
    }
    // 再次切断obj和对象之间的关联
    obj = null ;
    

    7、PhantomReference vs WeakReference

    PhantomReference 有两个好处:
    1、它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC, XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作)。
    2、它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中, 但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?
    这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用, 这将导致这一轮的 GC 无法回收这个对象并有可能引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用, 也就不会出现上述问题, 当然这是一个很极端的例子, 一般不会出现。

    参考资料:《疯狂Java讲义(第3版)》
    参考作者:舞熊科技 - 文章链接
    转发申明:我们不占有不侵权,我们只是好文的搬运工!转发请带上原文申明。

    相关文章

      网友评论

          本文标题:Java的对象引用类型

          本文链接:https://www.haomeiwen.com/subject/fkcaeftx.html