【java.lang.ref】Reference & ReferenceQueue & ReferenceHandler

本文详细介绍了Java的引用机制,包括为何需要引用机制、整体流程、不同引用类型(如SoftReference、WeakReference、PhantomReference、FinalReference)的作用以及它们在垃圾收集过程中的特殊处理。重点探讨了ReferenceQueue、pending-reference list和ReferenceHandler线程的工作原理,揭示了Java引用如何支持应用与垃圾收集器的交互,确保资源的优雅释放。
摘要由CSDN通过智能技术生成

目录

  • 零、前情概要
    • ref包内容
      • 相关线程
    • 系列目录
    • 上一章回顾
  • 一、why & how
    • 为什么要Java引用机制
    • Java引用机制的整体流程
      • 整体流程
      • 流程中需要探索的内容
  • 二、Treated specially by GC
  • 三、Reference概览
    • 整体概览
    • 源码中需要关注的点
  • 四、referent属性
    • 状态变化
    • FinalReference的例外
    • 状态变化图
  • 五、ReferenceQueue和next
  • 六、pending-reference list和discovered
  • 七、ReferenceHandler
  • 总结

 


零、前情概要

1.java.lang.ref包的内容

  • Reference & ReferenceQueue & ReferenceHandler 
    • SoftReference & WeakReference
    • PhantomReference
      • jdk.internal.ref.Cleaner
    • FinalReference
      • Finalizer & FinalizerThread
  • java.lang.ref.Cleaner
  • # 其中会涉及两个虚拟机线程:ReferenceHandler & FinalizerThread

2.系列目录

3.上一章回顾

《ref包简述》

  1. 首先通过两个API documentation点出了java.lang.ref包的核心——支持应用与垃圾收集器GC进行有限交互;
  2. 其次,简单浏览了java.lang.ref包中的四种弱引用(软引用、弱引用、虚引用和FinalReference)和核心逻辑;
  3. 最后,例举了包规范中的可达性;

 


一、why & how

1.为什么要Java引用机制

在没有Java引用的场景下,建立相关的资源对象之后,如果在一定的条件下不再需要资源对象(例如内存紧张、发生GC),那你要主动去释放,否则资源对象会一直占用着资源。

// resObj代表资源对象,除了自身分配的Java堆内存资源之外,其分配的可能还有其他资源
// 例如堆外的native memory、文件句柄、socket端口等等
ResourceObject resObj = new ResourceObject("分配资源");

例如强引用,因为建立的资源对象resObj一直都是强可达的,所以无论发生多少次GC,都不会回收resObj。

如果要回收resObj分配的资源:

  1. Java堆内存:分配给resObj的Java堆内存,你需要断开其强引用使其不可达,才会被GC回收;
  2. 其他资源:分配给resObj的其他资源例,如堆外的native memory、文件句柄、socket端口等,你也需要手动去释放;

如果忘记释放了呢?那会造成资源泄露。

有没有一种机制能够让你可以提前规避忘记释放资源所造成的风险,或者优雅一点地释放资源?

有,Java引用。

对于Java堆内存,在JVM中是由垃圾收集器GC来进行自动回收(GC在JVM中被称为自动存储管理系统,见《Java虚拟机规范》2.5.3章节)。

无论是Java堆内存,还是应用的其他资源,如果有一种机制能够与JVM GC进行交互联动,使得当GC发生的时候,根据不同的需求能够触发不同的资源回收操作,这个机制就是Java引用期望达到的效果。

前一章提过,支持应用与GC进行交互联动的机制有两个(一个是控制GC的发生,另一个是当GC发生的时候根据不同的需求触发不同的操作):

  • 显式GC(explicit garbage collection):即System.gc(),支持在应用中触发GC,一定程度上能控制GC的发生,因为本系列是讲Java引用,所以System.gc相关的参数、不同垃圾收集器下流程等不在本次范围之内;
  • Java引用:显式GC能够在一定程度上控制GC的发生,那当GC发生的时候,根据不同的引用类型,GC会触发不同的资源回收操作
    • Java堆内存:主要是软引用和弱引用,在《SoftReference & WeakReference》中描述;
    • 其他资源:主要是虚引用,在《PhantomReference & jdk.internal.ref.Cleaner》中描述;
    • 兜底机制:FinalReference,在《FinalReference & Finalizer & FinalizerThread》中描述;
    • java.lang.ref.Cleaner:Java 9提供的资源回收机制,在《java.lang.ref.Cleaner》中描述 

2.Java引用机制的整体流程

A.整体流程

  1. 当JVM中发生垃圾收集的时候,如果GC发现referent是弱可达的(一定要理解上一章可达性的内容和案例,四种弱可达的对象会被垃圾收集器特殊对待“Treated specially by GC”),那么GC会将封装了该referent的java.lang.ref.Reference及其子类的引用对象挂到pending-reference list上,这条链表是由GC维护的;
  2. 当GC将Reference对象挂到pending-reference list上时,发生一次线程间通信,会通知ReferenceHandler线程来取走这些节点并进行后续处理;
  3. ReferenceHandler线程拿到节点的后续处理如下:
    1. 如果节点Reference是jdk.internal.ref.Cleaner的实例,那么调用其方法jdk.internal.ref.Cleaner.clean(),这个方法会执行事先写好的释放资源的代码;
    2. 否则,如果是其他的引用类型的实例(SoftReference、WeakReference、PhantomReference、FinalReference),那么拿到其实例化时构造函数中传入的ReferenceQueue并调用该队列的enqueue函数,将引用对象入队列;
      1. 对于SoftReference和WeakReference,入队列其实没有太大意义,因为这俩对应着应用对Java堆内存的两种不同程度的需求,而Java堆内存的释放会由GC自动管理,所以通常使用软引用和弱引用的时候不会关联ReferenceQueue;
      2. 对于PhantomReference,对应着应用对其他资源的释放需求(例如堆外native memory、文件句柄、socket端口等),几乎没有直接使用PhantomReference的,而都是用其子类jdk.internal.ref.Cleaner,在ReferenceHandler中调用clean方法执行事先写好的释放资源的代码逻辑;
      3. 对于FinalReference,也几乎没有直接使用该类的,而都是直接使用其子类java.lang.ref.Finalizer,在队列中会发生第二次线程间通信,ReferenceHandler线程在将Reference对象入队列时,会通知FinalizerThread做进一步的处理——即调用FinalReference封装的referent重写的java.lang.Object的finalize()方法;
  4. 至于java.l
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值