来源:公众号:猿人谷

关于强引用、软引用、弱引用、幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用。

在java语言中,除了原始数据类型(boolean、byte、short、char、int、float、double、long)的变量,其他所有都是所谓的引用类型,指向各种不同的对象。理解这些引用的区别,对于掌握java对象生命周期和JVM内部相关机制非常有帮助。也有助于更深刻的理解底层对象生命周期、垃圾收集机制等,对设计可靠的缓存框架、诊断应用OOM等问题也大有裨益。

这四种应用主要的区别体现在对象不同的可达性状态和对垃圾收集的影响,他们之间的可达性状态可以参看下图:

1.强引用(strong reference)

强引用就是我们最常见的普通对象引用(如new 一个对象),只要还有强引用指向一个对象,就表明此对象还“活着”。在强引用面前,即使JVM内存空间不足,JVM宁愿抛出OutOfMemoryError运行时错误(OOM),让程序异常终止,也不会靠回收强引用对象来解决内存不足的问题。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,就意味着此对象可以被垃圾收集了。但要注意的是,并不是赋值为null后就立马被垃圾回收,具体的回收时机还是要看垃圾收集策略的。

如Object obj = new Object();

2.软引用(soft reference)

软引用相对强引用要弱化一些,可以让对象豁免一些垃圾收集。当内存空间足够的时候,垃圾回收器不会回收它。只有当JVM认定内存空间不足时才会去回收软引用指向的对象。JVM会确保在抛出OOM前清理软引用指向的对象,而且JVM是很聪明的,会尽可能优先回收长时间闲置不用的软引用指向的对象,对那些刚构建的或刚使用过的软引用指向的对象尽可能的保留。基于软引用的这些特性,软引用可以用来实现很多内存敏感点的缓存场景,即如果内存还有空闲,可以暂时缓存一些业务场景所需的数据,当内存不足时就可以清理掉,等后面再需要时,可以重新获取并再次缓存。这样就确保在使用缓存提升性能的同时,不会导致耗尽内存。

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

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
//有时候会返回null
sf.get(); 

通过上面的代码可以看出sf是对obj的一个软引用,当sf对象还没有被销毁前,sf.get()可以获取到这个对象,如果已被销毁,则返回null。

正确使用软引用的示例代码如下:

SoftReference<List<Foo>> ref = new SoftReference<List<Foo>>(new LinkedList<Foo>());

// somewhere else in your code, you create a Foo that you want to add to the list
List<Foo> list = ref.get();
if (list != null)
{
list.add(foo);
}
else
{
// list is gone; do whatever is appropriate
} 

在使用软引用的时候必须检查引用是否为null。因为垃圾收集器可能在任意时刻回收软引用,如果不做是否null的判断,可能会出现NullPointerException的异常。

总的来说,软引用是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。

3.弱引用(weak reference)

弱引用指向的对象是一种十分临近finalize状态的情况,当弱引用被清除的时候,就符合finalize的条件了。弱引用与软引用最大的区别就是弱引用比软引用的生命周期更短暂。垃圾回收器会扫描它所管辖的内存区域的过程中,只要发现弱引用的对象,不管内存空间是否有空闲,都会立刻回收它。如同前面我说过的,具体的回收时机还是要看垃圾回收策略的,因此那些弱引用的对象并不是说只要达到弱引用状态就会立马被回收。

基于弱引用的这些特性,弱引用同样可以应用在很多需要缓存的场景。

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
//有时候会返回null
wf.get();
//返回是否被垃圾回收器标记为即将回收的垃圾
wf.isEnQueued();

4.幻象引用(phantom reference)

幻象引用,也有被说成是虚引用或幽灵引用。幻象引用并不会决定对象的生命周期。即如果一个对象仅持有虚引用,就相当于没有任何引用一样,在任何时候都可能被垃圾回收器回收。不能通过它访问对象,幻象引用仅仅是提供了一种确保对象被finalize以后,做某些事情的机制(如做所谓的Post-Mortem清理机制),也有人利用幻象引用监控对象的创建和销毁。

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
//永远返回null
pf.get();
//返回是否从内存中已经删除
pf.isEnQueued(); 

  

幻象引用的get方法永远返回null,主要用于检查对象是否已经从内存中删除。

5.生存还是死亡

通过上面对四种引用类型的分析,你可能发现有些对象即使不可达,但也并非是“非死不可”的,这个时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后被一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统奔溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可。譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行。

6.总结

对象的可达性是JVM垃圾收集器决定如何处理对象的一个重要考虑指标

所有引用类型都是抽象类java.lang.ref.Reference的子类,子类里提供了get()方法。通过上面的分析中可以得知,除了幻象引用(因为get永远返回null),如果对象还没有被销毁,都可以通过get方法获取原有对象。其实有个非常关键的注意点,利用软引用和弱引用,我们可以将访问到的对象,重新指向强引用,也就是人为的改变了对象的可达性状态。所以对于软引用、弱引用之类,垃圾收集器可能会存在二次确认的问题,以确保处于弱引用状态的对象没有改变为强引用。

但是有个问题,如果我们错误的保持了强引用(比如,赋值给了static变量),那么对象可能就没有机会变回类似弱引用的可达性状态了,就会产生内存泄露。所以,检查弱引用指向对象是否被垃圾收集,也是诊断是否有特定内存泄露的一个思路,我们的框架使用到弱引用又怀疑有内存泄露,就可以从这个角度检查。

对于软引用、弱引用、幻象引用可以配合引用队列(ReferenceQueue)来使用,特别是幻象引用,get方法只返回null,如果再不指定引用队列,基本就没有任何意义了。

上面分析了四种引用类型的使用,熟悉这几种应用类型对深入理解JVM也大有裨益。

【JVM学习】3.深入解析强引用、软引用、弱引用、幻象引用的更多相关文章

  1. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  2. java基础知识再学习--集合框架-对象的强、软、弱和虚引用

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://zhangjunhd.blog.51cto.com/113473/53092 本文 ...

  3. Java中四种引用:强、软、弱、虚引用

    这篇文章非常棒:http://alinazh.blog.51cto.com/5459270/1276173 Java中四种引用:强.软.弱.虚引用 1.1.强引用当我们使用new 这个关键字创建对象时 ...

  4. Java:对象的强、软、弱、虚引用

    转自: http://zhangjunhd.blog.51cto.com/113473/53092 1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...

  5. Java:对象的强、软、弱和虚引用

    1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...

  6. Java对象的强、软、弱和虚引用详解

    1.对象的强.软.弱和虚引用 转自:http://zhangjunhd.blog.51cto.com/113473/53092/ 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无 ...

  7. Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器

    //转 http://blog.csdn.net/lyfi01/article/details/6415726 1.Java对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变 ...

  8. Java:对象的强、软、弱和虚引用[转]

    原文链接:http://zhangjunhd.blog.51cto.com/113473/53092/ 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法 ...

  9. Java对象的强、软、弱和虚引用+ReferenceQueue

    Java对象的强.软.弱和虚引用+ReferenceQueue 一.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足 ...

  10. Java:对象的强、软、弱和虚引用的区别

    1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...

随机推荐

  1. JPA技术之EntityManager使用方法

    Session bean or MD bean对Entity bean的操作(包括所有的query, insert, update, delete操作)都是通过EntityManager实例来完成的. ...

  2. 替换 Docker 或 Laradock 中 Debian 系统镜像源解决软件安装问题

    Docker Debian 镜像源替换 因多数默认的 Docker 镜像为国外的,而采用的镜像源也是国外的,故访问很慢,所以我们需要替换为国内的(比如阿里云或163等). 163 - Debian A ...

  3. 解决IDEA springBoot读取*.properties文件中文内容乱码的问题

    1. 配置 properties 文件 2. 读取 sex 属性输出到页面, 中文乱码 3. file --> settings 4. Editor --> File Encodings ...

  4. Rust中的Rc--引用计数智能指针

    大部分情况下所有权是非常明确的:可以准确的知道哪个变量拥有某个值.然而,有些情况单个值可能会有多个所有者.例如,在图数据结构中,多个边可能指向相同的结点,而这个结点从概念上讲为所有指向它的边所拥有.结 ...

  5. FFMPEG+SDL实现视频播放器

    一. 前言 基于学习ffmpeg和sdl,写一个视频播放器是个不错的练手项目. 视频播放器的原理很多人的博客都有讲过,这里出于自己总结的目的,还是会做一些概况. 二. 视频播放器基本原理 2.1 解封 ...

  6. linux内核链表剖析

    1.移植linux内核链表,使其适用于非GNU编译器 2.分析linux内核中链表的基本实现 移植时的注意事项 清除文件间的依赖 剥离依赖文件中与链表实现相关的代码 清除平台相关的代码(GNU C) ...

  7. robotframework-post request请求携带上一个请求返回的cookie

    公司的接口服务需要先登录,获取服务端的cookie后,在后续的请求中携带这个cookie才能够访问 在尝试用RF工具进行自动化接口测试时,发现先访问登录接口之后,接着请求其他接口时没有自动携带上次请求 ...

  8. soapui学习

    另外分享几个公开的Webservice站点,你可以随便招几个服务来测试 http://www.webservicex.net/WS/wscatlist.aspx http://www.service- ...

  9. 从网络服务生成Apex类

    使用WSDL2Apex从网络服务生成Apex类 如果某个网络服务被定义在WSDL文件中,而Salesforce必须使用SOAP和网络服务进行通信,则这种情况在某些时候会为开发者带来很多麻烦.为了简化S ...

  10. 徒手实现lower_bound和upper_bound

    STL中lower_bound和upper_bound的使用方法:STL 二分查找 lower_bound: ; ; //初始化 l ,为第一个合法地址 ; //初始化 r , 地址的结束地址 int ...