这一篇将介绍弱引用和幻像引用。

1、WeakReference

WeakReference也就是弱引用,弱引用和软引用类似,它是用来描述"非必须"的对象的,它的强度比软引用要更弱一些。被弱引用关联的对象只能生存到下一次垃圾收集发生之前,简言之就是:一旦发生GC必定回收被弱引用关联的对象,不管当前的内存是否足够。WeakReference类的定义如下:

public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}

WeakReference继承了Reference。在ReferenceProcessorStats ReferenceProcessor::process_discovered_references()方法中调用process_discovered_reflist()方法处理弱引用,如下:

// Weak references
size_t weak_count = 0;
{
// 传递的clear_referent的值为true
weak_count = process_discovered_reflist(_discoveredWeakRefs, NULL, true,
is_alive, keep_alive, complete_gc, task_executor);
}

调用的process_discovered_reflist()方法中会用后2个阶段处理弱引用,在之前已经介绍过,这里不再介绍。

2、PhantomReference

PhantomReference也就是幻像引用,它是所有引用类型中最弱的一种。一个对象是否关联到虚引用,完全不会影响该对象的生命周期,也无法通过虚引用来获取一个对象的实例。为对象设置一个虚引用的唯一目的是:能在此对象被垃圾收集器回收的时候收到一个系统通知。PhantomReference类的定义如下:

public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}

可以看到幻像引用的get()方法永远返回null,所以也就无法通过虚引用来获取一个对象的实例。

public static void demo() throws InterruptedException {
Object obj = new Object();
ReferenceQueue<Object> refQueue =new ReferenceQueue<>();
PhantomReference<Object> phanRef =new PhantomReference<>(obj, refQueue);
Object objg = phanRef.get();
// 这里拿到的是null
System.out.println(objg);
// 让obj变成垃圾
obj=null;
System.gc();
Thread.sleep(3000);
// GC后会将phanRef加入到refQueue中
Reference<? extends Object> phanRefP = refQueue.remove();
//这里输出true
System.out.println(phanRefP==phanRef);
}

从以上代码中可以看到,幻像引用能够在指向对象不可达时得到一个“通知”(其实所有继承Reference的类都有这个功能),需要注意的是GC完成后,phanRef.referent依然指向之前创建Object,也就是说Object对象一直没被回收!

而造成这一现象的原因在之前也介绍过: 对于Finalreference和Phantomreference来说,clear_referent 字段传入的值为false,意味着被这两种引用类型引用的对象,如果没有其他额外处理,在GC中是不会被回收的。

对于幻像引用来说,从refQueue.remove()得到引用对象后,可以调用clear()方法强行解除引用和对象之间的关系,使得对象下次可以GC时可以被回收掉。

在ReferenceProcessor::process_discovered_references()方法中调用process_discovered_reflist()方法处理幻像引用,如下:

// Phantom references
size_t phantom_count = 0;
{
// 传递的clear_referent的值为false
phantom_count = process_discovered_reflist(_discoveredPhantomRefs, NULL, false,
is_alive, keep_alive, complete_gc, task_executor);
}

调用的process_discovered_reflist()方法中会用后2个阶段处理幻像引用,在之前已经介绍过,这里不再介绍。

3、Cleaner

PhantomReference有两个比较常用的子类,如下:

(1)java.lang.ref.Cleaner:开发者用于在引用对象回收的时候触发一个动作,在JDK 9中将完全替代Object.finalize()方法。

(2)jdk.internal.ref.Cleaner:用于DirectByteBuffer对象回收的时候对于堆外内存的回收。ReferenceHandler线程会对pending链表中的jdk.internal.ref.Cleaner类型引用对象调用其clean()方法。

对于java.lang.ref.Cleaner类来说,举个例子如下:

import java.lang.ref.Cleaner;

public class CleaningExample implements AutoCloseable {
// A cleaner, preferably one shared within a library
private static final Cleaner cleaner = Cleaner.create(); static class State implements Runnable {
State() {
System.out.println("init");// initialize State needed for cleaning action
} public void run() {
System.out.println("clean");// cleanup action accessing State, executed at most once
}
} private final State state;
private final Cleaner.Cleanable cleanable; public CleaningExample() {
this.state = new State();
this.cleanable = cleaner.register(this, state);
} public void close() {
cleanable.clean();
} public static void main(String[] args) {
while(true) {
new CleaningExample();
}
}
}

本例中每次创建对象时,都会打印init;回收对象时,都会打印clean。  

下面介绍jdk.internal.ref.Cleaner类。

Cleaner继承自Java四大引用类型之一的幻像引用PhantomReference(众所周知,无法通过幻像引用获取与之关联的对象实例,且当对象仅被幻像引用引用时,在任何发生GC的时候,其均可被回收),通常PhantomReference与引用队列ReferenceQueue结合使用,可以实现幻像引用关联对象被垃圾回收时能够进行系统通知、资源清理等功能。如下图所示,当某个被Cleaner引用的对象将被回收时,JVM垃圾收集器会将此对象的引用放入到对象引用中的pending链表中,等待ReferenceHandler进行相关处理。其中,ReferenceHandler为一个拥有最高优先级的守护线程,会循环不断的处理pending链表中的对象引用,执行Cleaner的clean()方法进行相关清理工作,这在之前介绍ReferenceHandler类时介绍过,这里不再介绍。

Cleaner只有在清理逻辑足够轻量和直接的时候才适合使用Cleaner,繁琐耗时的清理逻辑将有可能导致ReferenceHandler线程阻塞从而耽误其它的清理任务。

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

10、HotSpot的对象模型(6)

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器

14、类的双亲委派机制

15、核心类的预装载

16、Java主类的装载

17、触发类的装载

18、类文件介绍

19、文件流

20、解析Class文件

21、常量池解析(1)

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2)

25、字段解析(3)

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍

28、方法解析

29、klassVtable与klassItable类的介绍

30、计算vtable的大小

31、计算itable的大小

32、解析Class文件之创建InstanceKlass对象

33、字段解析之字段注入

34、类的连接

35、类的连接之验证

36、类的连接之重写(1)

37、类的连接之重写(2)

38、方法的连接

39、初始化vtable

40、初始化itable

41、类的初始化

42、对象的创建

43、Java引用类型

44、Java引用类型之软引用(1)

45、Java引用类型之软引用(2)

46、Java引用类型之弱引用与幻像引用

作者持续维护的个人博客  classloading.com

关注公众号,有HotSpot源码剖析系列文章!

   

参考文章:

Java魔法类:Unsafe应用解析

Java引用类型之弱引用与幻像引用的更多相关文章

  1. 你不可不知的Java引用类型之——弱引用

    定义 弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型.在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收. 说明 弱 ...

  2. Java引用类型之软引用(2)

    下面接着上一篇介绍第2阶段和第3阶段的处理逻辑. 2.process_phase2() 第2个阶段移除所有的referent还存活的Reference,也就是从refs_list中移除Referenc ...

  3. Java引用类型之最终引用

    FinalReference类只有一个子类Finalizer,并且Finalizer由关键字final修饰,所以无法继承扩展.类的定义如下: class FinalReference<T> ...

  4. Java的四种引用类型之弱引用

    先说结论: 首先,Java中有四种引用类型:强引用.软引用.弱引用.虚引用.-- 在 Java 1.2 中添加的,见 package java.lang.ref; . 其次,这几个概念是与垃圾回收有关 ...

  5. Java中的四种引用类型,强引用,软引用,弱引用,虚引用

    对于Java中的垃圾回收机制来说,对象是否被回收的标准在于该对象是否被引用.因此,引用也是JVM进行内存管理的一个重要概念. Java中对象的引用一般有以下4种类型: 1强引用  2软引用  3弱引用 ...

  6. java强引用、软引用、弱引用、虚引用

    前言概述 在JDK1.2以前的版本中,当一个对象不被任何变量引用,那么程序就无法再使用这个对象.这就像在日常生活中,从商店购买了某样物品后,如果有用,就一直保留它,否则就把它扔到垃圾箱,由清洁工人收走 ...

  7. 【转载】 Java 7之基础 - 强引用、弱引用、软引用、虚引用

    原文地址:http://blog.csdn.net/mazhimazh/article/details/19752475 1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一 ...

  8. Java 7之基础 - 强引用、弱引用、软引用、虚引用

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

  9. Java 的强引用、弱引用、软引用、虚引用

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

随机推荐

  1. 【Floyd算法+贪心】 travel 计蒜客 - 45275

    题目: 有 n 个景点,从一个景点 i 旅行到另一个景点 j 要花费 Ai,j=Aj,i(n≤100),现在给出由 m(≤1e5) 个景点的组成序列 A,求:在所有 "有子序列 A 的序列中 ...

  2. 7.20试机测 T3 阶乘之和 暴力AC题解

    7.20试机测  T3 阶乘之和 暴力AC题解 题外话:此乃本蒟蒻发表的第一篇题解,大家多多关照,支持一下,谢谢 题面 3.阶乘之和(sum.pas/in/out) 问题描述: 给定一个非负整数 n, ...

  3. UWP 自定义密码框控件

    1. 概述 微软官方有提供自己的密码控件,但是控件默认的行为是输入密码,会立即显示掩码,比如 *.如果像查看真实的文本,需要按查看按钮. 而我现在自定义的密码控件是先显示你输入的字符2s,然后再显示成 ...

  4. 一分钟速学 | NMS, IOU 与 SoftMax

    非极大抑制 NMS的英文是Non-maximum suppression的缩写. 简单的说,就是模型给出了多个重叠在一起的候选框,我们只需要保留一个就可以了.其他的重叠的候选框就删掉了,效果可见下图: ...

  5. C语言中对文件的读写的一些浅显理解

    前述:基于上学期完成的数据结构的课程设计,对于老师的提出要求实现的基础上,自己在使用过程中发现每次打开程序都需要重新输入数据,于是便决定,将文件读写功能加入此次课程设计中,以下是我的一些心得和浅显理解 ...

  6. Jenkins部署jmx脚本

      针对jenkins+jmeter做接口自动化,jmeter完成测试脚本录入和撰写,而jenkins负责持续集成和报告收集.那么从零实现jenkins可以从以下角度实现 首先需要安装jenkins ...

  7. ionic 侧边栏实例

    侧边栏的使用范例: <body > <ion-side-menus> <!-- 中间内容 --> <ion-side-menu-content ng-cont ...

  8. MacOS下JDK8的安装与配置

    微信搜索"艺术行者",关注并回复关键词"jdk8"获取安装包和API文档资料! 一.安装环节 1.打开网页 https://www.oracle.com/jav ...

  9. Python游戏编程入门 中文pdf扫描版|网盘下载内附地址提取码|

    Python是一种解释型.面向对象.动态数据类型的程序设计语言,在游戏开发领域,Python也得到越来越广泛的应用,并由此受到重视. 本书教授用Python开发精彩游戏所需的[]为重要的该你那.本书不 ...

  10. 原生JS 实现点击按钮创建元素

    要求: 点击按钮,随机生成一个20px-100px宽度正方形,随机颜色,随机位置,不能出可视区域外 思路:(1)创建按钮,为按钮添加事件侦听 (2)触发事件,创建一个元素 (3)设置元素样式,包括大小 ...