`

深入探讨Java的引用(Reference)

 
阅读更多

概述

 

       要深入了解cache的问题,首先要明确和了解java的引用。java虽然有垃圾回收机制,但java也有内存泄露的时候。什么引起java内存泄露呢?要理解这个问题,需要明确java的引用。java不像C++,在java中没有指针,只有引用,但java的引用在某种程度上就像C++中披着外衣的指针。在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,Java引入了Java.lang.ref包。

 

         Java.lang.ref Java 类库中比较特殊的一个包,它提供了与 Java 垃圾回收器密切相关的引用类。这些引用类对象可以指向其它对象,但它们不同于一般的引用,因为它们的存在并不防碍 Java 垃圾回收器对它们所指向的对象进行回收。其好处就在于使者可以保持对使用对象的引用,同时 JVM 依然可以在内存不够用的时候对使用对象进行回收。因此这个包在用来实现与缓存相关的应用时特别有用。同时该包也提供了在对象的“可达”性发生改变时,进行提醒的机制。本文通过对该包进行由浅入深的介绍与分析,使读者可以加深对该包的理解,从而更好地利用该包进行开发。

 

1. java.lang.ref 包的介绍

 

       我们可以先来看一下 java.lang.ref 这个包的结构,如图 1 所示



     该包中各类的继承关系如图 2 所示,Reference 是一个抽象类,而 SoftReferenceWeakReferencePhantomReference 以及 FinalReference 都是继承它的具体类。



 

 

2. StrongReference, SoftReference, WeakReference 以及 PhantomReference 的特性及用法

 

1)强引用(StrongReference

 

 

        我们都知道 JVM 中对象是被分配在堆(heap)上的,当程序行动中不再有引用指向这个对象时,这个对象就可以被垃圾回收器所回收。这里所说的引用也就是我们一般意义上申明的对象类型的变量(如 String, Object, ArrayList 等),区别于原始数据类型的变量(如 int, short, long 等)也称为强引用。强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

 

      举例说明:


      清单 1. StrongReference usage

           String tag = new String("T");

       此处的 tag 引用就称之为强引用。强引用所指向的对象在任何时候都不会被系统回收,强引用可能导致内存泄漏。所以如果你要设计一个 java 缓存系统,可以考虑少使用强引用,因为这样会让你的程序在无法预知的情况下出现 OutOfMemoryError错误,而缓存化是可以允许数据丢失,而去持久化层拿数据的。

 

        强引用有以下特征:

 

  1. 强引用可以直接访问目标对象。
  2. 强引用所指向的对象在任何时候都不会被系统回收。
  3. 强引用可能导致内存泄漏。

2)软引用(SoftReference

 

        软引用(Soft  Reference)的主要特点是具有较强的引用功能。只有当内存不够的时候才回收这类内存,因此在内存足够的时候,他们通常不被回收。另外,这些引用对象还能保证在Java  抛出OutOfMemory异常之前,被设置为null。他可以用于内存敏感的高速缓存,实现。不限大小Cache的功能(无需remove),保证最大限度的使用内存而不引起OutOfMemory异常。比如将其 Soft Reference 无限地放入 hashmap 中,而不关心hashmap内的对象数量是否会撑爆heap,也不需要手动 remove。当Heap容量达到OOM触发条件时,VM会自动回收该map里的所有SoftReferences.

 

       具体使用一般是通过 SoftReference 的构造方法,将需要用弱引用来指向的对象包装起来。当需要使用的时候,调用 SoftReference get() 方法来获取。当对象未被回收时 SoftReference get() 方法会返回该对象的强引用。如下:

 

       清单 2. SoftReference usage

 

SoftReference<Bean> bean = new SoftReference<Bean>(new Bean("name", 10)); 
System.out.println(bean.get());// “name:10”

       软引用有以下特征:

 

  • 软引用使用 get() 方法取得对象的强引用从而访问目标对象。
  • 引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。
  • 软引用可以避免 Heap 内存不足所导致的异常。

       当垃圾回收器决定对其回收时,会先清空它的 SoftReference,也就是说 SoftReference get() 方法将会返回 null,然后再调用对象的 finalize() 方法,并在下一轮 GC 中对其真正进行回收。

 

       软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

 

3)弱引用(WeakReference

 

        WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似。其主要区别:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。完全可以通过和 SoftReference 一样的方式来操作 WeakReference,这里就不再复述。

 

         清单 3. WeakReference usage

WeakReference<Bean> bean = new WeakReference <Bean>(new Bean("name", 10)); 
System.out.println(bean.get());// “name:10”

   

         弱引用有以下特征:

 

  • 弱引用使用 get() 方法取得对象的强引用从而访问目标对象。
  • 一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。
  • 弱引用也可以避免 Heap 内存不足所导致的异常。

       弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

 

3)虚引用(PhantomReference

 

虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

 

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

 

所以具体用法和之前两个有所不同,它必须传入一个 ReferenceQueue 对象。当虚引用所引用对象被垃圾回收后,虚引用会被添加到这个队列中。如:

 

      清单 4. PhantomReference usage

 public static void main(String[] args) { 
     ReferenceQueue<String> refQueue = new ReferenceQueue<String>(); 
     PhantomReference<String> referent = new PhantomReference<String>(new String("T"), refQueue); 
     System.out.println(referent.get());// null 
    
     System.gc(); 
     System.runFinalization(); 
     System.out.println(refQueue.poll() == referent); //true 
 } 

 

 

值得注意的是,对于引用回收方面,虚引用类似强引用不会自动根据内存情况自动对目标对象回收,Client 需要自己对其进行处理以防 Heap 内存不足异常。

 

虚引用有以下特征:

 

  • 虚引用永远无法使用 get() 方法取得对象的强引用从而访问目标对象。
  • 虚引用所指向的对象在被系统内存回收前,虚引用自身会被放入 ReferenceQueue 对象中从而跟踪对象垃圾回收。     
  • 虚引用不会根据内存情况自动回收目标对象。

另外值得注意的是,其实 SoftReference, WeakReference 以及 PhantomReference 的构造函数都可以接收一个 ReferenceQueue 对象。当 SoftReference 以及 WeakReference 被清空的同时,也就是 Java 垃圾回收器准备对它们所指向的对象进行回收时,调用对象的 finalize() 方法之前,它们自身会被加入到这个 ReferenceQueue 对象中,此时可以通过 ReferenceQueue poll() 方法取到它们。而 PhantomReference 只有当 Java 垃圾回收器对其所指向的对象真正进行回收时,会将其加入到这个 ReferenceQueue 对象中,这样就可以追综对象的销毁情况。

 

         各种引用类型总结:

 

 下表对于各种引用类型的特征进行了小结:

 

引用类型

取得目标对象方式

垃圾回收条件

是否可能内存泄漏

强引用

直接调用

不回收

可能

软引用

通过 get() 方法

视内存情况回收

不可能

弱引用

通过 get() 方法

永远回收

不可能

虚引用

无法取得

不回收

可能

 

注意:如果想使用这些相对强引用来说较弱的引用来进行对象操作的时候,就必须保证没有强引用指向被操作对象。否则将会被视为强引用指向,不会具有任何的弱引用的特性

 

3. StrongReference, SoftReference, WeakReference 以及 PhantomReference 的各种特性实验分析

 

 

         为了更好的描述它们的特性,先以表格进行归纳,再以示例程序加以说明。

 

  • JVM 使用 Oracle Java SE6
  • 首先将 JVM 运行环境的初始以及最大 Heap 数设到最低以便更明显的看出结果
    3. 设置 JVM 运行环境初始值
    <!--[if !supportLineBreakNewLine]-->

     

    接下来就开始我们的实验。

    2:各个引用在 GC 后是否被回收?

    类型

    GC 后是否回收

    示例代码

    运行结果

    StrongReference

    不回收

    见清单 3

    name:10

    SoftReference

    不回收

    见清单 4

    name:10

    WeakReference

    回收

    见清单 5

    name:10

    PhantomReference

    N/A

    N/A

    N/A

     

    清单 4

    public static void main(String[] args) { 
         Bean bean = new Bean("name", 10); 
         System.gc(); 
         System.runFinalization(); 
         System.out.println(bean);// “name:10”
     } 
    
     

    总结:强引用所指向的对象在任何时候都不会被系统回收。结果输入

     

    清单 5

     public static void main(String[] args) { 
         SoftReference<Bean> bean = new SoftReference<Bean>(new Bean("name", 10)); 
         System.gc(); 
         System.runFinalization(); 
         System.out.println(bean.get());// “name:10”
     } 
    
     

    总结:软引用所指向的对象会根据内存使用情况来决定是否回收,这里内存还充足,所以不会被回收。


    清单 6

     public static void main(String[] args) { 
         WeakReference<Bean> bean = new WeakReference<Bean>(new Bean("name", 10)); 
         System.gc(); 
         System.runFinalization(); 
         System.out.println(bean.get());// “null”
     }
    
     

    总结:弱引用所指向的对象只要进行 GC,就会自动进行回收,get() 返回 null

    3:各个引用创建大量对象时是否导致 Heap 不足异常?

    类型

    是否抛出异常

    示例代码

    运行结果

    StrongReference

    抛出异常

    见清单 6

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    SoftReference

    不抛异常,之前的引用自动清空并返回 null

    见清单 7

    null

    WeakReference

    同上

    见清单 8

    null

    PhantomReference

    抛出异常

    见清单 9

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

     

    清单 7

    public static void main(String[] args) { 
         Bean[] referent = new Bean[100000]; 
         for (int i=0;i<referent.length;i++){ 
             referent[i] = new Bean("mybean:" + i,100);// 抛 Exception 
         } 
     }
    
     

    总结:在新开辟 100000 Bean 对象时,由于强引用永远不会被系统回收,当最大 Heap 阈值达到 2m 时,系统就会报出 Heap 不足的异常。


    清单 8

    public static void main(String[] args) { 
         Reference<Bean>[] referent = new SoftReference[100000]; 
         for (int i=0;i<referent.length;i++){ 
             referent[i] = new SoftReference<Bean>(new Bean("mybean:" + i,100)); 
         } 
        
         System.out.println(referent[100].get());// “null”
     } 
    

    总结:在新开辟 100000 Bean 对象时,由于软引用会视内存使用情况来判断是否自动回收,所以当最大 Heap 阈值达到 2m 时,系统自动回收最前面开辟的对象,取第 100 个对象时,返回为 null

     

    清单 9 
    <!--[endif]-->
     public static void main(String[] args) { 
         Reference<Bean>[] referent = new WeakReference[100000]; 
         for (int i=0;i<referent.length;i++){ 
             referent[i] = new WeakReference<Bean>(new Bean("mybean:" + i,100)); 
         } 
    
         System.out.println(referent[100].get());// “null”
     }
    
     

    总结:WeakReference SoftReference 具有相同的特性,也会视内存使用情况来判断是否自动回收。取第 100 个对象时,返回为 null

     

    清单 10

    public static void main(String[] args) { 
         Reference<Bean>[] referent = new PhantomReference[100000]; 
         ReferenceQueue<Bean> queue = new ReferenceQueue<Bean>(); 
         for (int i=0;i<referent.length;i++){ 
             referent[i] = new PhantomReference<Bean>(new Bean("mybean:" + i,100), queue);// throw Exception 
         } 
    
         System.out.println(referent[100].get()); 
     } 
    
    
    

     

     

    总结:PhantomReference 类似强引用,它不会自动根据内存情况自动对目标对象回收,所以这里在 Heap 里不断开辟新空间,当达到 2m 阈值时,系统报出 OutOfMemoryError 异常。

     

     FinalReference 以及 Finzlizer

     

    FinalReference 作为 java.lang.ref 里的一个不能被公开访问的类,又起到了一个什么样的作用呢?作为他的子类, Finalizer 又在垃圾回收机制里扮演了怎么样的角色呢?实际上,FinalReference 代表的正是 Java 中的强引用,如这样的代码 :

     

       Bean bean = new Bean();

     

           在虚拟机的实现过程中,实际采用了 FinalReference 类对其进行引用。而 Finalizer,除了作为一个实现类外,更是在虚拟机中实现一个 FinalizerThread,以使虚拟机能够在所有的强引用被解除后实现内存清理。让我们来看看 Finalizer 是如何工作的。首先,通过声明 FinalizerThread,并将该线程实例化,设置为守护线程后,加入系统线程中去。

     

    清单 11

    static { 
            ThreadGroup tg = Thread.currentThread().getThreadGroup(); 
            for (ThreadGroup tgn = tg; 
                 tgn != null; 
                 tg = tgn, tgn = tg.getParent()); 
           Thread finalizer = new FinalizerThread(tg); 
           finalizer.setPriority(Thread.MAX_PRIORITY - 2); 
           finalizer.setDaemon(true); 
           finalizer.start(); 
     } 
    

     

           GC 的过程中,当一个强引用被释放,由系统垃圾收集器标记后的对象,会被加入 Finalizer 对象中的 ReferenceQueue 中去,并调用 Finalizer.runFinalizer() 来执行对象的 finalize 方法。

     

    清单 12

     

    private void runFinalizer() { 
         synchronized (this) { 
            if (hasBeenFinalized()) return; 
            remove(); 
         } 
         try { 
             Object finalizee = this.get(); 
             if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { 
             invokeFinalizeMethod(finalizee); 
     /* 注意,这里需要清空栈中包含该变量的的 slot, 
                       ** 从而来减少因为一个保守的 GC 实现所造成的变量未被回收的假象 */ 
             finalizee = null; 
        } 
     } catch (Throwable x) { } 
       super.clear(); 
     } 
    

     

            注意,标记处所调用的 invokeFinalizeMethod native 方法,由于 finalize 方法在 Object 类中被声明为 protected,这里必须采用 native 方法才能调用。随后通过将本地强引用设置为空,以便使垃圾回收器清理内存。

             可以看到,通过这样的方法,Java 将四种引用对象类型:软引用 (SoftReference),弱引用 (WeakReference),强引用 (FinalReference),虚引用 (PhantomReference) 平等地对待,并在垃圾回收器中进行统一调度和管理。

     

    不同 Java 虚拟机上的表现与分析

     

    让我们来回顾一下四种引用类型的表现以及在垃圾回收器回收清理内存时的表现 .

     

    1. 软引用 (SoftReference), 引用类型表现为当内存接近满负荷 , 或对象由 SoftReference.get() 方法的调用没有发生一段时间后 , 垃圾回收器将会清理该对象 . 在运行对象的 finalize 方法前 , 会将软引用对象加入 ReferenceQueue 中去 .
    2. 弱引用 (WeakReference), 引用类型表现为当系统垃圾回收器开始回收时 , 则立即会回收该对象的引用 . 与软引用一样 , 弱引用也会在运行对象的 finalize 方法之前将弱引用对象加入 ReferenceQueue.
    3. 强引用 (FinalReference), 这是最常用的引用类型 . JVM 系统采用 Finalizer 来管理每个强引用对象 , 并将其被标记要清理时加入 ReferenceQueue, 并逐一调用该对象的 finalize() 方法 .
    4. 虚引用 (PhantomReference), 这是一个最虚幻的引用类型 . 无论是从哪里都无法再次返回被虚引用所引用的对象 . 虚引用在系统垃圾回收器开始回收对象时 , 将直接调用 finalize() 方法 , 但不会立即将其加入回收队列 . 只有在真正对象被 GC 清除时 , 才会将其加入 Reference 队列中去 .

    这里比较两个比较典型的 JVM 环境,Oracle Java SE6 IBM JDK 6。采用了如下的测试代码 :

     

    清单 13. RefTestObj

    public class RefTestObj { 
         private int id; 
    
         public int getId() { 
         return id; 
     } 
    
     public void setId(int id) { 
         this.id = id; 
     } 
    
     @Override 
     public int hashCode() { 
         return super.hashCode(); 
     } 
    
     @Override 
     public String toString() { 
         return super.toString() + "[id=" + this.id + "]"; 
     } 
    
     @Override 
     protected void finalize() { 
         System.out.println("Object [" + this.hashCode() + "][
         id=" + this.id + "] come into finalize"); 
         try { 
            super.finalize(); 
         } catch (Throwable e) { 
             e.printStackTrace(); 
         } 
      } 
     } 
    

     

    清单 14. RefMainThread

    import java.lang.ref.PhantomReference;
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.SoftReference;
    import java.lang.ref.WeakReference;
    
    public class RefMainThread {
    	public static void main(String[] args) {
    		// 创建三种不同的引用类型所需对象
    		RefTestObj softRef = new RefTestObj();
    		RefTestObj weakRef = new RefTestObj();
    		RefTestObj phanRef = new RefTestObj();
    
    		softRef.setId(1);
    		weakRef.setId(2);
    		phanRef.setId(3);
    
    		ReferenceQueue<RefTestObj> softRefQueue = new ReferenceQueue<RefTestObj>();
    		ReferenceQueue<RefTestObj> weakRefQueue = new ReferenceQueue<RefTestObj>();
    		ReferenceQueue<RefTestObj> phanRefQueue = new ReferenceQueue<RefTestObj>();
    
    		SoftReference<RefTestObj> softRefObj = new SoftReference<RefTestObj>(
    				softRef, softRefQueue);
    		WeakReference<RefTestObj> weakRefObj = new WeakReference<RefTestObj>(
    				weakRef, weakRefQueue);
    		PhantomReference<RefTestObj> phanRefObj = new PhantomReference<RefTestObj>(
    				phanRef, phanRefQueue);
    
    		// 打印正常情况下三种对象引用
    		print(softRefObj);
    		print(weakRefObj);
    		print(phanRefObj);
    
    		// 将对象清空
    		softRef = null;
    		weakRef = null;
    		phanRef = null;
    
    		// 打印引用队列及 get() 方法所能取到的对象自身
    		if (softRefObj != null) {
    			System.out.println("Soft Reference Object run get():"
    					+ softRefObj.get());
    			System.out.println("Check soft queue:" + softRefQueue.poll());
    		}
    
    		if (weakRefObj != null) {
    			System.out.println("Weak Reference Object run get():"
    					+ weakRefObj.get());
    			System.out.println("Check weak queue:" + weakRefQueue.poll());
    		}
    
    		if (phanRefObj != null) {
    			System.out.println("Phantom Reference Object run get():"
    					+ phanRefObj.get());
    			System.out.println("Check Phantom queue:" + phanRefQueue.poll());
    		}
    
    		// 开始执行垃圾回收
    		System.gc();
    		System.runFinalization();
    
    		// 检查队列,是否已经被加入队列,是否还能取回对象
    		if (softRefObj != null) {
    			System.out.println("Soft Reference Object run get():"
    					+ softRefObj.get());
    			System.out.println("Check soft queue:" + softRefQueue.poll());
    		}
    
    		if (weakRefObj != null) {
    			System.out.println("Weak Reference Object run get():"
    					+ weakRefObj.get());
    			System.out.println("Check weak queue:" + weakRefQueue.poll());
    		}
    
    		if (phanRefObj != null) {
    			System.out.println("Phantom Reference Object run get():"
    					+ phanRefObj.get());
    			System.out.println("Check Phantom queue:" + phanRefQueue.poll());
    		}
    
    		// 对于虚引用对象,在经过多次 GC 之后, 才会加入到队列中去
    		Reference<? extends RefTestObj> mynewphan = null;
    		int refCount = 1;
    		while (mynewphan == null) {
    			mynewphan = phanRefQueue.poll();
    			System.gc();
    			System.runFinalization();
    			if (mynewphan != null) {
    				System.out.println("Check Phantom queue:" + mynewphan);
    				System.out.println("Count for " + refCount + " times");
    				break;
    			}
    			refCount++;
    		}
    	}
    
    	public static void print(Reference<RefTestObj> ref) {
    		RefTestObj obj = ref.get();
    		System.out.println("The Reference is " + ref.toString()
    				+ " and with object " + obj + " which is "
    				+ (obj == null ? "null" : "not null"));
    	}
    }

     

          通过执行 RefMainThread, 我们可以清晰地根据打印结果看到对象在内存中被加入队列 , 以及调用 finalize 方法的顺序及过程 .为了测试不同的 JVM 环境并消除其他因素的印象 , 本例采用的背景环境均为 Windows2003 下的 32bit JVM.

     

            首先采用了环境为 Oracle Java SE 6 update 23 进行测试 , 结果如下 :

     

    清单 15. Oracle Java SE 6 update 23 下测试结果

     

    The Reference is java.lang.ref.SoftReference@c17164 and 
     with object RefTestObj@1fb8ee3[id=1] which is not null 
     The Reference is java.lang.ref.WeakReference@61de33 and 
     with object RefTestObj@14318bb[id=2] which is not null 
     The Reference is java.lang.ref.PhantomReference@ca0b6 and with object null which is null 
     Soft Reference Object run get():RefTestObj@1fb8ee3[id=1] 
     Check soft queue:null 
     Weak Reference Object run get():RefTestObj@14318bb[id=2] 
     Check weak queue:null 
     Phantom Reference Object run get():null 
     Check Phantom queue:null 
     Object [27744459][id=3] come into finalize 
     Object [21174459][id=2] come into finalize 
     Soft Reference Object run get():RefTestObj@1fb8ee3[id=1] 
     Check soft queue:null 
     Weak Reference Object run get():null 
     Check weak queue:java.lang.ref.WeakReference@61de33 
     Phantom Reference Object run get():null 
     Check Phantom queue:null 
     Check Phantom queue:java.lang.ref.PhantomReference@ca0b6 
     Count for 2 times 
    

     

          可以看到 , 当运行了系统回收后 , 虚引用与弱引用被回收 , 由于内存并不吃紧 , 软引用依然保持原样 . 弱引用立即被加入了队列 , 而虚引用则在循环两次的手动调用 GC 后被加入了队列 . 其次 , 采用的环境是 IBM JDK 6, 结果如下 :

     

    清单 16. IBM JDK 6 下测试结果

    The Reference is java.lang.ref.SoftReference@3a2c3a2c and 
     with object RefTestObj@391e391e[id=1] which is not null 
     The Reference is java.lang.ref.WeakReference@3a303a30 and 
     with object RefTestObj@39203920[id=2] which is not null 
     The Reference is java.lang.ref.PhantomReference@3a353a35 
     and with object null which is null 
     Soft Reference Object run get():RefTestObj@391e391e[id=1] 
     Check soft queue:null 
     Weak Reference Object run get():RefTestObj@39203920[id=2] 
     Check weak queue:null 
     Phantom Reference Object run get():null 
     Check Phantom queue:null 
     Object [958544162][id=3] come into finalize 
     Object [958413088][id=2] come into finalize 
     Soft Reference Object run get():RefTestObj@391e391e[id=1] 
     Check soft queue:null 
     Weak Reference Object run get():null 
     Check weak queue:java.lang.ref.WeakReference@3a303a30 
     Phantom Reference Object run get():null 
     Check Phantom queue:null 
     Object [958282014][id=1] come into finalize 
     ............ 
    

     

          程序运行到这里进入了无限循环,必须手动终止。比对上下两份结果可以看到,当多次运行系统垃圾回收后,IBM JVM 将软引用一并加入了回收队列中,并运行了其 finalize 方法。另外,即使经过很多次系统垃圾回收,虚引用也没有被加入到队列中去。不知道这是不是 IBM JVM 的一个小小的 BUG 所在。

    结论

    • SoftReference Oracle JVM 的表现满足规范,只当内存不足时才进行回收。而 IBM JVM 的策略则更为积极,在内存尚且充足的情况下也进行了回收,值得注意。
    • PhantomReference Oracle JVM 的表现满足规范,执行 finalize 后若干次 GC 就被添加到了 Queue 中。而 IBM JVM 则始终没有被添加到 Queue 中导致了死循环。所以在使用 PhantomReference 时出现类似的情况时,可以考虑是否是因为使用了不同 JVM 所导致。
  • 大小: 25.3 KB
  • 大小: 52.8 KB
  • 大小: 25.3 KB
  • 大小: 52.8 KB
  • 大小: 79.9 KB
分享到:
评论

相关推荐

    Java功底之Reference

    本文将深入探讨`Reference`类及其相关知识点。 首先,我们需要了解Java中的垃圾收集机制。Java的自动内存管理主要依赖于垃圾收集器,它负责找出不再被程序使用的对象并回收其占用的内存。默认情况下,当一个对象...

    深入探讨 java.lang.ref 包1

    本文将深入探讨这个包中的四种主要引用类型:StrongReference、SoftReference、WeakReference 和 PhantomReference,以及它们的特性、用法和作用。 首先,强引用(StrongReference)是我们在日常编程中最常见的引用...

    深入探讨 java-lang-ref 包.docx

    总结,java.lang.ref 包中的四种引用类型——StrongReference、SoftReference、WeakReference 和 PhantomReference,提供了不同的内存管理策略,适应不同的场景需求。强引用是最常用的引用类型,但在处理大对象或...

    java 引用相关文档

    这个“java 引用相关文档”可能包含了深入探讨Java引用的各种类型及其特性的内容。在这里,我们将详细讲解Java中的四种引用类型:强引用、软引用、弱引用和虚引用,以及它们在内存管理和垃圾收集中的作用。 1. **强...

    java引用的demo

    本示例“java引用的demo”将深入探讨四种不同类型的引用:强引用、软引用、弱引用和虚引用。这些引用类型对于理解和优化内存管理至关重要,特别是在Android开发中,因为良好的内存管理能够提升应用性能并防止内存...

    Java引用类型编程开发技术共13页.pdf.zip

    这篇13页的PDF文档,很可能是深入探讨了Java中的引用类型及其在开发中的应用技术。在这里,我们将对Java引用类型的一些关键知识点进行详细的阐述。 1. 类(Class):Java是一种面向对象的语言,类是创建对象的蓝图...

    Java中的软引用弱引用和虚引用.docx

    本文将深入探讨Java中三种特殊的引用类型——软引用(Soft Reference)、弱引用(Weak Reference)以及虚引用(Phantom Reference),并分析它们如何帮助我们更好地管理内存资源。 #### 二、基础知识回顾 在深入了解这三...

    浅谈java面向对象和引用

    本文将围绕这两个概念进行深入探讨,并针对初学者常见的疑惑点进行解答。 #### Java面向对象基础 Java是一种完全面向对象的语言,几乎所有的功能都是通过类和对象来实现的。在Java中,类(Class)是一种定义对象的...

    java引用 对象 属性等数据类型知识

    这些概念构成了Java程序的基础,让我们深入探讨一下。 1. 引用(References): 在Java中,引用是一种变量,它存储了对象在内存中的地址,而非对象的实际值。当我们创建一个对象时,例如`Dog myDog = new Dog();`...

    c#调用java WEB服务

    接下来,我们将深入探讨如何创建和部署Java Web服务以及如何使用C#在ASP.NET中调用这些服务: 1. 创建Java Web服务: - 创建一个Web应用项目:这可以通过使用IDE如Eclipse或IntelliJ来完成,创建一个新的Maven或...

    java内存管理问题及解决办法

    本文将深入探讨Java内存管理机制,包括垃圾回收、内存分配与释放、内存泄漏及其预防措施,以及四种引用类型的特点和应用场景。 首先,Java内存分为堆内存(Heap)和栈内存(Stack)。堆内存主要用于存储对象实例,...

    springmvc 其他引用资源(reference)

    这里我们将深入探讨 Spring MVC 的核心概念、配置、控制器、数据绑定、视图解析以及其他关键特性。 1. **核心概念**: - DispatcherServlet:Spring MVC 的核心组件,负责接收请求并分发给合适的处理器。 - ...

    十分钟理解Java中的弱引用编程开发技术共3页.pdf.z

    本文将深入探讨这一主题,帮助你在这十分钟内建立起对弱引用的理解。 首先,我们需要知道Java的内存区域分为堆内存和栈内存。其中,对象主要存储在堆内存中,而栈内存则用于存储基本类型和对象引用。当一个对象被...

    java-jls8.pdf

    在深入探讨Java 8的语法和词法基础的同时,文档还介绍了各种语法表示,以及它们在Java 8中的应用和变化。本文档是由Java语言的几位主要设计者共同撰写,包括James Gosling、Bill Joy、Guy Steele和Gilad Bracha等,...

    强应用软引用弱引用与垃圾收集的关系1

    本文将深入探讨强引用、软引用、弱引用以及幻象引用这四种引用类型,以及它们与垃圾收集的关系。 1. **强引用(Strong Reference)**: - 强引用是Java中最常见的引用类型,它代表了一个对象的直接访问路径。只要...

    Java参数传递PPT

    本PPT深入探讨了这一主题,旨在帮助学习者彻底理解Java中的参数传递方式。 首先,我们要回顾面向对象的基本概念。在Java中,类(Class)是对象的蓝图,它定义了对象可能具有的属性(如name、age、gender)和方法...

    浅谈Java中的四种引用方式的区别

    本文将深入探讨这四种引用——强引用、软引用、弱引用和虚引用,以及它们在内存管理和垃圾回收中的作用。 1. **强引用(StrongReference)** - 强引用是最常见的引用类型,它代表了最常见的对象引用。只要一个对象...

    java用JNA调用dll实例,包含各种参数调用

    本实例将深入探讨如何使用JNA调用DLL,并涵盖了各种参数类型,包括基本数据类型、指针、结构体、回调函数、字符串以及数组。 首先,理解JNA的基本工作原理至关重要。JNA通过映射DLL中的函数到Java类的方法来实现...

Global site tag (gtag.js) - Google Analytics