首页 > 其他分享 >浅析WeakHashMap

浅析WeakHashMap

时间:2022-11-10 20:34:46浏览次数:59  
标签:WeakHashMap 回收 GC 引用 Entry 浅析 size


在Java或者是Android编程中,我们一般都会使用到Map,比如HashMap这样的具体实现。更高级一点,我们可能会使用WeakHashMap。

WeakHashMap其实和HashMap大多数行为是一样的,只是WeakHashMap不会阻止GC回收key对象(不是value),那么WeakHashMap是怎么做到的呢,这就是我们研究的主要问题。

在开始WeakHashMap之前,我们先要对弱引用有一定的了解。

在Java中,有四种引用类型

  • 强引用(Strong Reference),我们正常编码时默认的引用类型,强应用之所以为强,是因为如果一个对象到GC Roots强引用可到达,就可以阻止GC回收该对象
  • 软引用(Soft Reference)阻止GC回收的能力相对弱一些,如果是软引用可以到达,那么这个对象会停留在内存更时间上长一些。当内存不足时垃圾回收器才会回收这些软引用可到达的对象
  • 弱引用(WeakReference)无法阻止GC回收,如果一个对象时弱引用可到达,那么在下一个GC回收执行时,该对象就会被回收掉。
  • 虚引用(Phantom Reference)十分脆弱,它的唯一作用就是当其指向的对象被回收之后,自己被加入到引用队列,用作记录该引用指向的对象已被销毁

这其中还有一个概念叫做引用队列(Reference Queue)

  • 一般情况下,一个对象标记为垃圾(并不代表回收了)后,会加入到引用队列。
  • 对于虚引用来说,它指向的对象会只有被回收后才会加入引用队列,所以可以用作记录该引用指向的对象是否回收。

WeakHashMap如何不阻止对象回收呢



private static final class Entry<K, V> extends WeakReference<K> implements
Map.Entry<K, V> {
int hash;
boolean isNull;
V value;
Entry<K, V> next;
interface Type<R, K, V> {
R get(Map.Entry<K, V> entry);
}
Entry(K key, V object, ReferenceQueue<K> queue) {
super(key, queue);
isNull = key == null;
hash = isNull ? 0 : key.hashCode();
value = object;
}

如源码所示,

  • WeakHashMap的Entry继承了WeakReference。
  • 其中Key作为了WeakReference指向的对象
  • 因此WeakHashMap利用了WeakReference的机制来实现不阻止GC回收Key

如何删除被回收的key数据呢

在Javadoc中关于WeakHashMap有这样的描述,当key不再引用时,其对应的key/value也会被移除。

那么是如何移除的呢,这里我们通常有两种假设策略

  • 当对象被回收的时候,进行通知
  • WeakHashMap轮询处理时效的Entry

而WeakHashMap采用的是轮询的形式,在其put/get/size等方法调用的时候都会预先调用一个poll的方法,来检查并删除失效的Entry



void poll() {
Entry<K, V> toRemove;
while ((toRemove = (Entry<K, V>) referenceQueue.poll()) != null) {
removeEntry(toRemove);
Log.d(LOGTAG, "removeEntry=" + toRemove.value);
}
}

为什么没有使用看似更好的通知呢,我想是因为在Java中没有一个可靠的通知回调,比如大家常说的finalize方法,其实也不是标准的,不同的JVM可以实现不同,甚至是不调用这个方法。

当然除了单纯的看源码,进行合理的验证是检验分析正确的一个重要方法。

这里首先,我们定义一个MyObject类,处理一下finalize方法(在我的测试机上可以正常调用,仅仅做为辅助验证手段)



class MyObject(val id: String) : Any() {
protected fun finalize() {
Log.i("MainActivity", "Object($id) finalize method is called")
}
}

然后是调用者的代码,如下



private val weakHashMap = WeakHashMap<Any, Int>()
var count : Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
dumpWeakInfo()
fab.setOnClickListener { view ->
//System.gc()// this seldom works use Android studio force gc stop
weakHashMap.put(MyObject(count.toString()), count)
count ++
dumpWeakInfo()
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
}

fun dumpWeakInfo() {
Log.i("MainActivity", "dumpWeakInfo weakInfo.size=${weakHashMap.size}")
}

我们按照如下操作

  • 点击fab控件,每次对WeakhashMap对象增加一个Entry,并打印WeakHashMap的size 执行3此
  • 在没有强制触发GC时,WeakHashMap对象size一直会增加
  • 手动出发Force GC,我们会看到MyObject有finalize方法被调用
  • 再次点击fab空间,然后输出的WeakHashMap size急剧减少。
  • 同样我们收到在WeakHashMap增加的日志也会输出
1
2
3
4
5
6
7
8
9
10
I/MainActivity(10202): dumpWeakInfo weakInfo.size=1
I/MainActivity(10202): dumpWeakInfo weakInfo.size=2
I/MainActivity(10202): dumpWeakInfo weakInfo.size=3
I/MainActivity(10202): Object(2) finalize method is called
I/MainActivity(10202): Object(1) finalize method is called
I/MainActivity(10202): Object(0) finalize method is called
I/WeakHashMap(10202): removeEntry=2
I/WeakHashMap(10202): removeEntry=0
I/WeakHashMap(10202): removeEntry=1
I/MainActivity(10202): dumpWeakInfo weakInfo.size=1

注意:System.gc()并不一定可以工作,建议使用Android Studio的Force GC

完整的测试代码可以访问这里 ​​https://github.com/androidyue/WeakHashMapSample​

标签:WeakHashMap,回收,GC,引用,Entry,浅析,size
From: https://blog.51cto.com/u_3987305/5842014

相关文章

  • 卡特兰数浅析
    背景:之前不熟悉的知识点,所以现在来总结一下概念卡特兰数是一个经常出现在组合数学中的一个数列1,1,2,5,14,42,132,429,1430,4862,...以比利时的数学家欧仁......
  • 浅析Spring事务实现原理
    SQL事务实现简介首先我们来了解下,最简单的事务是怎么实现的呢?以JDBC为例,当一个数据库Connection对象创建后,其会默认自动提交事务;每次执行SQL语句时,如果成功,就会向数据库自......
  • 浅析布隆过滤器
    一、什么是BloomFilter布隆过滤器(英语:BloomFilter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。主要用于判断一个元素是否在一个集合......
  • 浅析深度学习在图像处理中的应用趋势及常见技巧
    导读 图像处理领域是深度学习和机器视觉领域重要的研究分支,本文第一部分将介绍深度学习中图像处理的常用技巧,第二部分则会浅析深度学习中图像处理的主流应用。导言近年以来......
  • 浅析DDD
    什么是DDD软件开发不是一蹴而就的事情,我们不可能在不了解产品(或行业领域)的前提下进行软件开发,在开发前,通常需要进行大量的业务知识梳理,而后到达软件设计的层面,最后才是开发......
  • 通用文档信息提取模型浅析
    文章目录​​1.前言与痛点​​​​2.通用信息提取模型技术分析​​​​1.技术介绍​​​​2.原理分析​​​​1.LayoutDetection(视觉检测模块):​​​​2.OCR(文字识别......
  • OpenStack Neutron浅析
    1.基础知识1.1防火墙(firewall)防火墙是依照特定的规则来控制进出它的网络流量的网络安全系统。一个典型的场景是在一个受信任的内网和不受信任的外网比如Internet之间......
  • Python yield 使用浅析
    之前了解了生成器的概念,带有yield的函数在Python中被称之为generator(生成器),那么应该什么时候使用呢?举个例子:简单输出斐波那契數列前N个数deffab(max):n,a,b=......
  • 浅析云边端协同与算力调度在AI视频检测场景中的应用意义
    人工智能在医疗卫生、能源动力、交通航天、语言图像识别等领域发挥着重要作用,在安防等领域也同样值得期待。人工智能、深度学习、视频结构化技术、物联网技术,大数据分析等变......
  • 浅析基于云-边-端协同架构的AI算力资源智能调度能力
    随着AI、云计算、边缘计算、大数据、物联网等技术的不断发展、数据的不断增加,基于云、边、端协同架构的部署需求也越来越多。TSINGSEE青犀视频的智能分析网关/云平台,不仅融......