在Java的世界里,对象的存在层次,也有三六九等,充满了阶层之间的嘲弄。强软弱虚各种引用,对于熟悉Java的同学一定不会感到陌生,它们随着等级的降低,越来越没存在感。平常使用的对象,大多数就是强引用的;而软引用和弱引用,则经常在一些堆内缓存框架中用到。

那虚引用呢?传说中的幽灵引用,是不是就如同它的名字一样,一无是处呢?

三种引用
首先,我们来回顾一下其他三种引用的类型和用途。

Strong references
当内存空间不足,系统撑不住了,JVM 就会抛出 OutOfMemoryError 错误。即使程序会异常终止,这种对象也不会被回收。这种引用属于最普通最强硬的一种存在,只有在和 GC Roots 断绝关系时,才会被消灭掉。

这种引用,你每天的编码都在用。例如:new 一个普通的对象。

Object obj = new Object()
这种方式可能是有问题的。假如你的系统被大量用户(User)访问,你需要记录这个 User 访问的时间。可惜的是,User 对象里并没有这个字段,所以我们决定将这些信息额外开辟一个空间进行存放。

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

可以看到,这种特性非常适合用在缓存技术上。比如网页缓存、图片缓存等。

Guava 的 CacheBuilder,就提供了软引用和弱引用的设置方式。在这种场景中,软引用比强引用安全的多。

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

Weak references
弱引用对象相比较软引用,要更加无用一些,它拥有更短的生命周期。

当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。弱引用拥有更短的生命周期,在 Java 中,用 java.lang.ref.WeakReference 类来表示。

怪异的虚引用
以上几个引用级别都很好理解,但是虚引用是个例外。虚引用可以使用下面的代码定义:

Object object = new Object();
ReferenceQueue queue = new ReferenceQueue();
// 虚引用,必须与一个引用队列关联
PhantomReference pr = new PhantomReference(object, queue);
但是当你想取出其中的值时(get),得到的却总是null。

//JDK源码
/**
* Returns this reference object’s referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* {@code null}.
*
* @return {@code null}
*/
public T get() {
return null;
}
虚引用主要用来跟踪对象被垃圾回收的活动。

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象之前,把这个虚引用加入到与之关联的引用队列中。

程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

桃花源深处
在hotspot的jvm中,有一个叫做cleaner的类,其实就是虚引用典型的应用。可以看到Cleaner是直接简单粗暴的继承了PhantomReference,所以它本质上就是一个虚引用,只不过多了一些便捷的操作。

那么这个类是在什么地方用到的呢?大家手上应该都有jdk的源代码,追踪一下,发现最后竟然是DirectByteBuffer用到了它。

直接内存,一直是一个看起来非常高大上的名词,基本上和高性能挂钩,但也容易产生内存泄漏。由于直接内存,是属于堆外内存的,所以垃圾回收的时候,就不能靠JVM的那一套垃圾回收算法进行清理。

事实上,由于DirectByteBuffer可能会被使用较长时间,熬过了年轻代的各种回收,就会进入老年代。这时候就比较麻烦了,这些引用对象,要在下一轮Old GC或者Full GC才能触发,如果你的老年代空间较大,触发回收的操作就需要等很久很久。问题是,在这段时间内,虽然这些堆外内存不再使用了,但它仍然占用着较大的物理空间,最后造成严重的浪费甚至崩溃。

对堆外内存不是很熟悉的同学,可以看我以前的一张图。或者直接看这篇文章。通过-XX:MaxDirectMemorySize可以限制直接内存的使用上限。

《一图解千愁,jvm内存从来没有这么简单过!》

那么这些堆外内存是如何进行回收的呢?这就是Cleaner的作用。Cleaner通过next和prev构造了一个典型的链表,但它本身是没有任何逻辑的,因为它的清理逻辑都在thunk方法中。

cleaner = Cleaner.create(this, new Deallocator(base, size, cap));

public void clean() {
if (remove(this)) {
try {
this.thunk.run();
也就是Deallocator = De allocator。其中,传入的base,就是靠unsafe类申请的堆外内存地址引用(仅仅是个地址),有了引用和容量,其实我们就能够在回收的时候定位到真正的堆外内存块。就像Deallocator做的一样。

public void run() {
if (address == 0) {
// Paranoia
return;
}
unsafe.freeMemory(address);
address = 0;
Bits.unreserveMemory(size, capacity);
}
机制上没什么问题,关键要看它们是怎么联系起来的。这种问题,当然是要靠其他线程完成,这里就是 ReferenceHandler 。很熟悉的名字,你每次使用jstack命令导出堆栈,都会看到它。

Thread handler = new ReferenceHandler(tg, “Reference Handler”);
/* If there were a special system-only priority greater than

  • MAX_PRIORITY, it would be used here
    */
    handler.setPriority(Thread.MAX_PRIORITY);
    handler.setDaemon(true);
    handler.start();
    真正去工作的方法,是tryHandlePending,然后在这里,调用Cleaner的clean方法,进而调用真正的清理方法,释放堆外内存。它会从虚引用注册的队列里,取出新的对象,然后判断是不是Cleaner类型,如果是,就进行一次清理。

End
这就是虚引用。它存在的唯一目的,就是在回收的时候,能够被感知到,以便进行更深层次的清理。在commons-io包的FileCleaningTracker类中,同样有继承了虚引用的 Tracker 类,用来跟踪后续文件的一些清理工作。这个没存在感的小小虚引用,默默的承担起最后一道防线,是系统正常运行的有效保证。

不要小看它,它无处不在。因为你的每一个JVM进程,都跑着一个叫做 Reference Handler 的线程呢。

作者简介: 小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。我的个人微信xjjdog0,欢迎添加好友,进一步交流。

那些令人发燥的JAVA虚引用相关推荐

  1. java虚引用_深入了解JAVA 虚引用

    定义 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个.一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获 ...

  2. java 虚引用 作用_Java的强引用、弱引用、软引用和虚引用,以及用途

    Java种除了基本数据类型,其它数据类型都是引用的数据类型.而应用数据类型根据生命周期的长短又分为:强引用.弱引用.软引用和需引用(幻象引用),我们平时基本上只用到强引用类型,而其他的引用类型我们也就 ...

  3. java虚引用作用_深入理解Java中的引用(二)——强软弱虚引用

    深入理解Java中的引用(二)--强软弱虚引用 在上一篇文章中介绍了Java的Reference类,本篇文章介绍他的四个子类:强引用.软引用.弱引用.虚引用. 强引用(StrongReference) ...

  4. java强引用弱引用_Java 的强引用、弱引用、软引用、虚引用

    1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.如下: Object o=new Object(); // 强引用 当内存空间 ...

  5. java的虚引用_你不可不知的Java引用类型之——虚引用

    定义 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个.一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获 ...

  6. Java 四种引用类型:强引用、软引用、弱引用、虚引用

    以前学习强软弱虚引用的时候,只是走马观花看看博客,并没有自己写代码去实践.去证明,导致每次看完后,过不了多久就忘了,后来下定决心,一定要自己敲敲代码,这样才能让印象更加深刻,古人云:纸上得来终觉浅,绝 ...

  7. java 静态内部类 弱引用_Java基础 强引用、弱引用、软引用、虚引用

    前言 在ThreadLocal源码中,其中嵌套类ThreadLocalMap中的Entry继承了WeakReferenc.Java中提供这四种引用类型主要有两个目的:第一是可以让程序员通过代码的方式决 ...

  8. java对象的强引用,软引用,弱引用和虚引用

    java对象的强引用,软引用,弱引用和虚引用 标签:              javaoutofmemoryerror生活虚拟机jvmjdk 2011-05-05 21:00             ...

  9. java弱引用弱点_终于有人把Java强、软、弱、虚四种引用知识点整理出来了

    强引用就是指在程序代码之中普遍存在的,指创建一个对象并把这个对象赋给一个引用变量,并没有像其它三种引用一样有一个就具体的类来描述.对于强引用对象,即使内存不足,JVM宁愿抛出OutOfMemoryEr ...

最新文章

  1. java数组如何pop_Js数组的操作push,pop,shift,unshift等方法详细介绍
  2. python2执行程序内存溢出导致被killed的问题因果分析
  3. 零件分组pascal程序
  4. utf8编码转换脚本
  5. Fragment 键盘弹出方式设置
  6. 代码中特殊的注释技术——TODO、FIXME和XXX的用处(转)
  7. 利用Maven逆向工程生成mybatis映射文件
  8. kdj指标主要看哪个值_KDJ绝密买卖法则
  9. 《2021新青年生长力报告》:水果青年、农货青年、设计青年,哪个最潮?
  10. IDDD 实现领域驱动设计-上下文映射图及其相关概念
  11. 使用邮件客户端整合日常信息
  12. 礼物linux项目,好玩的Linux命令,将礼品包在盒子中
  13. 小明医声发布,看AI技术秀得飞起
  14. hbuilder发生了错误请参阅日志文件1545838571032
  15. 34、HTML制做奖多多采购页面(仅供参考)
  16. python预测子女身高_Python 孩子身高预测
  17. 工业机器人(9)-- Matlab机器人工具箱之创建单臂/双臂机器人SDH/MDH方法
  18. 如何继承字走三国武器
  19. java 上传文件-生成文件首页缩略图 生成pdf 抓取图片
  20. centos 硬盘挂载

热门文章

  1. CSS单行、多行文本溢出隐藏
  2. 工大校园邮箱申请流程
  3. 在github上托管属于自己的网页
  4. 第十一届 蓝桥杯 单片机设计与开发项目 决赛
  5. Espresso:自定义Idling Resource
  6. 2022化工自动化控制仪表考试题及答案
  7. JAV----------数组操作
  8. 根据地理位置获取经纬度
  9. R7-7735HS和i5-13500H 差距 锐龙R77735HS和酷睿i513500H对比
  10. 科研狗citavi/mendeley+word使用技巧外加读论文瞎琢磨