【java.lang.ref】SoftReference & WeakReference

目录

  • 零、前情概要
    • ref包内容
    • 系列目录
    • 上一章回顾
  • 一、WeakReference
    • 适用场景
    • 测试案例
    • 应用举例
  • 二、SoftReference的适用场景
  • 三、量化软引用“内存紧张”条件
    • 软引用的时间戳clock&timestamp
    • 虚拟机参数-XX:SoftRefLRUPolicyMSPerMB
    • hotspot软引用清除策略ReferencePolicy
    • Java引用预处理ReferenceProcessor
    • 小结
  • 四、软引用测试验证
    • -client参数
    • 测试用例
    • 注意事项
      • CMS
      • G1
  • 总结

 


零、前情概要

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.上一章回顾

  • 理解可达性
  • Java引用的适用场景
  • Java引用的整体流程:两个生产者消费者模型
  • Reference源码概览
  • 理解referent属性的可达性变化是推动Java引用流程前进的基石
  • next属性和ReferenceQueue
  • discovered属性和pending-reference list
  • ReferenceHandler线程

 


一、WeakReference

1.适用场景

API documentation: WeakReference (Java SE 15 & JDK 15) (oracle.com)

Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings.

Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues.

正如API文档中所述,WeakReference的适用场景:规范化映射——Weak references are most often used to implement canonicalizing mappings.

最常见的两个应用例子就是ThreadLocal和WeakHashMap。

当GC发现一个对象弱可达,那GC会自动clear。也就是说只要发生GC,弱可达的对象就会被处理。

如第一章包规范中弱可达的描述,再次明确一下弱可达:弱引用封装的referent不是强可达、软可达,只能通过封装referent的弱引用对象找到。

ResourceObj resObj = new ResourceObj("分配资源");
Reference reference = new WeakReference(resObj);
// 执行到这里,资源对象resObj既是强可达,也是弱可达
resObj = null; // 断开资源对象resObj的强引用,是其变成真正的弱可达,即只能通过若引用对象才能找到

如第二章Reference中Java引用的处理流程所述,弱引用的处理流程:

  • 当GC的时候,垃圾收集器发现referent弱可达
  • GC会将封装该referent的Java引用对象挂到pending-reference list上并clear referent(断开referent的弱引用,即Reference.referent = null)
  • 然后线程间通信,唤醒阻塞在waitForReferencePendingList上的ReferenceHandler线程
  • ReferenceHandler线程拿到pending-reference list,然后循环处理该链表上的所有引用节点
  • 如果是弱引用,则拿到其实例化时关联的队列,并调用queue.enqueue将引用入队列,第一个生产者-消费者模型结束,进入队列等待下一步的处理(进一步处理就需要自定义逻辑,参考WeakHashMap)
  • 如果没有关联队列,则流程结束

2.测试案例

环境:JDK15 -server -Xmx200M -Xms200M

思想:在保证内存足够的情况下,通过System.gc触发垃圾收集,然后调试观察对应的WeakReference是否会进入关联队列

验证:只要发生GC,弱可达对象就会被处理

资源对象User:一个User实例大概占用4M内存,User没有重写finalize方法(重写finalize方法会经历至少两次GC,这个在后面FinalReference的时候再展开讲)

/**
 * 一个User实例大概占用4M Java堆内存
 */
public class User {
    private int[] num = new int[1000 * 1000];

    private String username;

    public User(String username) {
	this.username = username;
    }
    
    @Override
    public String toString() {
        return this.username + hashCode();
    }
}

测试及结果:创建100个User的WeakReference数组,创建第11个的时候通过System.gc触发垃圾收集

  • 当通过System.gc触发垃圾收集的时候,已经创建的11个弱引用对象全部进入关联的引用队列
  • 当创建完100个User,一个User实例大概需要4M内存,100个大概需要400M内存,在限定-Xmx200m的条件下,不会触发oom,因为大部分的引用对象都会被GC clear referent
public class Main {
    public static void main(String[] args) {
	    ReferenceQueue<User> queue = new ReferenceQueue<User>();
	    testWeak(queue);
    }
    
    private static void testWeak(ReferenceQueue<User> queue) {
	    @SuppressWarnings("unchecked")
	    WeakReference<User>[] userWeakReference = new WeakReference[100];
	    for (int i = 0; i < userWeakReference.length; i++) {
	        userWeakReference[i] = new WeakReference<User>(new User(i + ""), queue);
	        // 11个弱引用进入引用队列
	        if (i == 10)
		        System.gc();
	    }
	    System.out.println(userWeakReference[10].get());
    }
}

调试结果

3.应用举例

Weak references are most often used to implement canonicalizing mappings.”

正如API documentation中描述,弱引用一般的适用场景是“规范化映射”,WeakReference常见有两个案例:ThreadLocal和WeakHashMap。

这两个案例值得关注的点是如何处理引用对象的强引用关系。

两个案例中:Entry extends WeakReference,key作为referent,弱引用Entry存放在Map.table[]中(Entry[] table),虽然作为key的referent成为弱可达之后会被GC处理,但是在Map.table[]中是持有Entry的强引用的。也就是referent的资源被回收了(key的资源被回收了),但是WeakReference是强可达的不会被GC回收(Entry在Map.table[]中是强可达的,不会被GC回收)。

此时,两个案例中都会主动的去找key == null的Entry,然后table[i] = null,就是主动断开WeakReference(即Entry)的强引用,让Entry不可达被释放掉:

  • ThreadLocal中,这个逻辑在java.lang.ThreadLocal.ThreadLocalMap.expungeStaleEntry(int)方法中,ThreadLocal的get、set、remove方法满足条件的都会调用expungeStaleEntry清除失效的entry;
  • 在WeakHashMap中,这个逻辑在java.util.WeakHashMap.expungeStaleEntries()方法中,WeakHashMap的get、set、remove、resize这些方法都会走到这里清除失效的entry;

 


二、SoftReference的适用场景

API documentation:SoftReference (Java SE 15 & JDK 15) (oracle.com)

Soft reference objects, which are cleared at the discretion of the garbage collector in response to memory demand. Soft references are most often used to implement memory-sensitive caches.

Suppose that the garbage collector determines at a certain point in time that an object is softly reachable. At that time it may choose to clear atomically all soft references to that object and all soft references to any other softly-reachable objects from which that object is reachable through a chain of strong references. At the same time or at some later time it will enqueue those newly-cleared soft references that are registered with re

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值