基础 | Java中四种引用的区别

在JDK 1.2以前,Java中的引用定义为:如果引用类型的数据中存储的数值代表的是另一块内存的起始地址,则这块内存即代表着一个引用。故在该定义下,一个Java对象仅有被应用和未被引用两种状态。

在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用和虚引用。这四种引用与Java虚拟机的垃圾回收机制紧密关联,建议重点关注。


Java中四种引用有什么区别?

参考答案:

Java将引用分为强引用、软引用、弱引用和虚引用四种,这四种引用强度依次逐渐减弱。

下面分别从四种引用的基本概念、具体实现、生命周期和应用场景四个方面进行对比分析。

强引用(Strong Reference):

  • 基本概念:在程序代码中普遍存在的,类似于“Object obj = new Object()”形式的引用。
  • 具体实现:创建一个对象并将其赋值给一个引用变量。
  • 生命周期:垃圾收集器永远不会回收掉强引用所关联着的对象,除非将该对象的所有引用全部置为null。
  • 应用场景:用的太多,以至于我说不出来(撒手状)。

软引用(Soft Reference):

  • 基本概念:描述一些还有用,但并非必需的对象。
  • 具体实现:JDK提供了SoftReference类来实现。
  • 生命周期:在系统将要发生内存溢出异常之前,将会把软引用所关联着的对象列进回收范围内并进行第二次回收,若回收后仍没有足够内存则抛出内存溢出异常。
  • 应用场景:用于实现内存敏感的高速缓存,如网页缓存和图片缓存等。

注意:使用软引用能防止内存泄漏,增强程序的健壮性。

弱引用(Weak Reference):

  • 基本概念:也描述非必需对象,强度比软引用更弱。
  • 具体实现:JDK提供了WeakReference类来实现。
  • 生命周期:无论系统内存是否足够,垃圾收集器工作时都会受到只被弱引用关联着的对象。
  • 应用场景:多将对象的弱引用作为HashMap的key值,以实现在不需要该对象时,只需要在程序中将其强引用置为null,而无需手动将其从HashMap中移除(由垃圾收集器实现,WeakHashMap即是基于该原理)。

虚引用(Phantom Reference):

  • 基本概念:也称幽灵引用或幻影引用,是最弱的一种引用关系。
  • 具体实现:JDK提供了PhantomReference类来实现。
  • 生命周期:虚引用并不会影响其所关联对象的生存时间,也无法通过虚引用来取得一个对象实例。
  • 应用场景:虚引用可以在对象被收集器回收时收到一个系统通知,即多用于在对象销毁前执行一些操作,如资源释放等。

扩展代码阅读

软引用的具体使用:

// 建立软引用并丢弃强引用
MyObject obj = new MyObject();
SoftReference softRef = new SoftReference(obj);
obj = null; //丢弃强引用

// 通过软应用获取强引用
// 若软引用所关联的对象未被回收则重新获得强引用
// 若已被回收,则anotherObj = null
MyObject anotherObj = (MyObject)softRef.get();

/**
 * 若get()返回null,则说明软引用所关联的对象obj已被回收
 * softRef对象不再具有存在的价值
 * 可使用ReferenceQueue来将其清除,以避免发生内存泄漏
 */
MyObject obj = new MyObject();
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(obj, queue);
obj = null;

// 若ref所关联的对象被回收,则ref会自动添加到queue队列中去。

弱引用的具体使用:与软引用雷同,其可以和一个引用对列(ReferenceQueue)联合使用,如果弱引用所关联的对象被JVM回收,该弱引用就会被添加到与之关联的引用对列中。

虚引用的具体使用:必须和引用对列关联使用,具体流程如下:

  • 对于虚引用所关联的对象,垃圾收集器工作时会将该虚引用添加到与之关联的引用对列中;
  • 程序通过判断引用对列中是否已经加入了虚引用,来了解被引用对象是否将要被回收;
  • 若将要被回收,则可以在其被回收之前采取必要的操作。

虚引用的测试代码如下:

Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, queue);
obj=null;

while(true){
    System.out.printf("pf.get() = %d, isEnqueued: %b\r\n", pf.get(), pf.isEnqueued());
    if(pf.isEnqueued())
        break;
    System.gc();
    Thread.sleep(1000);                
}

// pf.get() = null, isEnqueued: false
// pf.get() = null, isEnqueued: true

扩展面试题

问:Java扩展为四种引用的目的是什么?

  • 有利于Java虚拟机进行垃圾回收;
  • 可以让程序员通过代码的方式来决定某些对象的生命周期。

推荐阅读

  • Java的反射与动态代理
  • String、StringBuffer与StringBuilder
  • Exception与Error
  • final、finally与finalize
  • Java序列化与反序列化的底层实现

欢迎关注

Java名企面试吧,每天10点24分,我们不见不散!

丙子先生的宗旨是,每天以短篇幅讲高频面试题,不增加太多负担,但需要持之以恒。

能力有限,欢迎指教!

基础 | Java中四种引用的区别_第1张图片

你可能感兴趣的:(Java)