Java 2 平台引入了 java.lang.ref 包,这个包下面包含了几个Reference相关的类,Reference相关类将Java中的引用也映射成一个对象,这些类还提供了与垃圾收集器(garbage collector)之间有限的交互。

Reference引用类的几种类型

在jvm中,一个对象如果不再被使用就会被当做垃圾给回收掉,判断一个对象是否是垃圾,通常有两种方法:引用计数法和可达性分析法。不管是哪一种方法判断一个对象是否是垃圾的条件总是一个对象的引用是都没有了。

JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用、软引用、弱引用、虚引用4 种。下面就介绍下这些引用类型的区别。

强引用

如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。下面的代码中str就是一个强引用。

  1. public void test1(){
  2. String str = new String("程序员自由之路");
  3. }

软引用(SoftReference)

内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。

上面只是很简单的说了下:当系统没有足够的内存时会回收软引用对象。但是具体什么才是内存不够?具体的回收具体是什么?如果想要了解具体的情况,大家可以参考这篇文章。我简单总结了下,软引用对象具体的回收策略如下:

如果已经没有引用指向软引用对象,那么这个对象会被JVM回收;

如果还有软引用指向这个软引用对象,就判断在某段之间之内(_max_interval),有没有调用过SoftReference的get方法,如果在_max_interval时间内没调用过get方法,那么即使还有软引用指向这个对象,JVM也会回收这个对象,如果在_max_interval时间内调用过get方法,那么就不会回收这个对象。

_max_interval具体的时间是根据JVM的可用内存动态计算出来的,如果JVM的可用内存比较大,那么_max_interval的值也比较大,如果JVM的可用内存比较小,那么max_interval也会比较小。

我自己写了一段代码来展示软引用对象回收的过程。为了让堆内存迅速耗尽,我将最大内存设置为-Xmx5m。

  1. public static void main(String[] args) throws InterruptedException {
  2. SoftReference<String> reference = new SoftReference<>(new String("自由之路..."));
  3. List<String> list = new ArrayList<>();
  4. while (true) {
  5. for (int i = 0; i < 10000; i++) {
  6. // 这边的对象都是强引用,不会被回收
  7. list.add(new String("自由之路"));
  8. }
  9. // 暂停一段时间,为了让_max_interval时间段检测生效
  10. // 没有这段暂停的话,JVM不会回收软引用对象,因为一直有线程在快速地调用软引用的get方法
  11. TimeUnit.MILLISECONDS.sleep(10);
  12. String s = reference.get();
  13. if (s == null) {
  14. logger.info("OMG, reference is gone...");
  15. }else {
  16. logger.info(s);
  17. }
  18. }
  19. }

代码的执行效果,如下:

  1. 13:36:52.322 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  2. 13:36:52.372 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  3. 13:36:52.385 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  4. 13:36:52.397 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  5. 13:36:52.412 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  6. 13:36:52.423 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  7. 13:36:52.435 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  8. 13:36:52.488 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  9. 13:36:52.499 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  10. 13:36:52.555 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  11. // 从下面开始,软引用对象已经被虚拟机回收了。
  12. 13:36:52.666 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
  13. 13:36:54.750 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
  14. 13:36:58.686 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
  15. // 系统已经不能再分配出内存空间,直接报OutOfMemoryError
  16. Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
  17. at com.csx.demo.spring.boot.dao.UserMapperTest.main(UserMapperTest.java:54)

弱引用(WeakReference)

如果一个对象具有弱引用,在垃圾回收时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。

关于弱引用,我也写了个Bug代码,展示弱引用对象的回收过程。

  1. public static void main(String[] args) throws InterruptedException {
  2. WeakReference<String> reference = new WeakReference<>(new String("自由之路..."));
  3. List<String> list = new ArrayList<>();
  4. while (true) {
  5. list.add(new String("自由之路"));
  6. String s = reference.get();
  7. if (s == null) {
  8. logger.info("OMG, reference is gone...");
  9. } else {
  10. logger.info(s);
  11. }
  12. }
  13. }

代码的执行结果如下:

  1. 13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  2. 13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  3. 13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  4. 13:50:54.015 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - 自由之路...
  5. // 这边GC已经将弱引用对象回收
  6. 13:50:54.051 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
  7. 13:50:54.051 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...
  8. 13:50:54.051 [main] INFO com.csx.demo.spring.boot.dao.UserMapperTest - OMG, reference is gone...

关于WeakReference,Java中一个比较典型的应用就是:WeakHashMap。关于这个类的使用情况大家可以参考这篇文章

虚引用(PhantomReference)

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个。一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获得一个对象实例。

使用虚引用的目的就是为了得知对象被GC的时机,所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。这个虚引用对于对象而言完全是无感知的,有没有完全一样,但是对于虚引用的使用者而言,就像是待观察的对象的把脉线,可以通过它来观察对象是否已经被回收,从而进行相应的处理。

在<<深入理解Java虚拟机>>3.2.3中有这么一句话

为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

  1. public class Test {
  2. public static boolean isRun = true;
  3. @SuppressWarnings("static-access")
  4. public static void main(String[] args) throws Exception {
  5. String abc = new String("abc");
  6. System.out.println(abc.getClass() + "@" + abc.hashCode());
  7. final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
  8. new Thread() {
  9. public void run() {
  10. while (isRun) {
  11. Object obj = referenceQueue.poll();
  12. if (obj != null) {
  13. try {
  14. Field rereferent = Reference.class
  15. .getDeclaredField("referent");
  16. rereferent.setAccessible(true);
  17. Object result = rereferent.get(obj);
  18. System.out.println("gc will collect:"
  19. + result.getClass() + "@"
  20. + result.hashCode() + "\t"
  21. + (String) result);
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }
  27. }
  28. }.start();
  29. PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
  30. referenceQueue);
  31. abc = null;
  32. Thread.currentThread().sleep(3000);
  33. System.gc();
  34. Thread.currentThread().sleep(3000);
  35. isRun = false;
  36. }
  37. }

一个线程一直再检测回收队列中有没有被回收的引用。如果有被回收的引用,进行一些操作。

引用队列(ReferenceQueue)

作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法:

  1. ReferenceQueue queue = new ReferenceQueue();
  2. SoftReference ref = new SoftReference(object, queue);

那么当这个SoftReference所软引用的对象被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。

在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收,于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。


  1. SoftReference ref = null;
  2. while ((ref = (EmployeeRef) q.poll()) != null) {
  3. // 清除ref
  4. }

参考

Java中的Reference类使用的更多相关文章

  1. 带有静态方法的类(java中的math类)

    带有静态方法的类通常(虽然不一定是这样)不打算被初始化. 可以用私有构造函数来限制非抽象类被初始化. 例如,java中的math类.它让构造函数标记为私有,所以你无法创建Math的实例.但Math类却 ...

  2. java中的File类

    File类 java中的File类其实和文件并没有多大关系,它更像一个对文件路径描述的类.它即可以代表某个路径下的特定文件,也可以用来表示该路径的下的所有文件,所以我们不要被它的表象所迷惑.对文件的真 ...

  3. Java基础(43):Java中的Object类与其方法(转)

    Object类 java.lang.Object java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. O ...

  4. java中基于TaskEngine类封装实现定时任务

    主要包括如下几个类: 文章标题:java中基于TaskEngine类封装实现定时任务 文章地址: http://blog.csdn.net/5iasp/article/details/10950529 ...

  5. Java中的Unsafe类111

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  6. Java中遍历实体类(处理MongoDB)

    在实际过程中,经常要将实体类进行封装,尤其是处理数据库的过程中:因此,对于遍历实体类能够与数据库中的一行数据对应起来. 我是使用的环境是Spring boot,访问的数据库时MongoDB 实体类遍历 ...

  7. java中遍历实体类,获取属性名和属性值

    方式一(实体类): //java中遍历实体类,获取属性名和属性值 public static void testReflect(Object model) throws Exception{ for ...

  8. Java中的BigDecimal类精度问题

    bigdecimal 能保证精度的原理是:BigDecimal的解决方案就是,不使用二进制,而是使用十进制(BigInteger)+小数点位置(scale)来表示小数,就是把所有的小数变成整数,记录小 ...

  9. java 中常用的类

    java 中常用的类 Math Math 类,包含用于执行基本数学运算的方法 常用API 取整 l  static double abs(double  a) 获取double 的绝对值 l  sta ...

随机推荐

  1. 消失的两个数字(1-N缺两个数)

    给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字.你能在 O(N) 时间内只用 O(1) 的空间找到它们吗? 以任意顺序返回这两个数字均可. 示例 1: 输入: [1]输出: [2,3 ...

  2. Markdown语法+Typora快捷键

    1. Markdown语法 1.1 代码块生成 // 对于代码块,使用"```+编程语言"即可生成书写对应代码块的区域 // JS代码块 ​```javascript // Jav ...

  3. diamond收集插件的自定义

    diamond是与graphite配合使用的一个数据收集的软件,关于这个配置的资料很多,使用起来也比较简单,详细的安装和配置会在后面的关于整套监控系统的文章里面写到,本篇是专门讲解怎么自定义这个数据收 ...

  4. (二)廖师兄springboot微信点餐虚拟机说明文档

    虚拟机 VirtualBox-5.1.22  系统 CentOS7.3账号 root密码 123456 软件:jdk 1.8.0_111nginx 1.11.7mysql 5.7.17redis 3. ...

  5. 分布式监控系统之Zabbix网络发现

    前文我们了解了zabbix的宏,自定义item和模板的相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/14013331.html:今天我们来了解下zab ...

  6. mysql学习笔记1(mysql的基本架构)

    mysql基本架构图 如图所示: 1 . MySQL 可以分为 Server 层和存储引擎层两部分 Server 层包括连接器.查询缓存.分析器.优化器.执行器等,涵盖 MySQL 的大多数核心服务功 ...

  7. 把token放入请求头

    1.jq 2.vue.js

  8. Ubuntu sudo 出现unable to resolve host 解决方法

    Ubuntu sudo 出现unable to resolve host 解决方法 Ubuntu环境, 假设这台机器名字叫abc(机器的hostname), 每次执行sudo 就出现这个警告讯息: s ...

  9. http host头攻击漏洞

    原文地址: https://www.zhuyilong.fun/tech/handel_httphost_attack.html 漏洞描述 为了方便的获得网站域名,开发人员一般依赖于HTTP Host ...

  10. 【数据结构模版】可持久化线段树 && 主席树

    浙江集训Day4,从早8:00懵B到晚21:00,只搞懂了可持久化线段树以及主席树的板子.今天只能记个大概,以后详细完善讲解. 可持久化线段树指的是一种基于线段树的可回溯历史状态的数据结构.我们想要保 ...