淺析Java中的WeakHashMap
WeakHashMap
在Java或者是Android編程中,我們一般都會使用到Map,比如HashMap這樣的具體實現(xiàn)。更高級一點,我們可能會使用WeakHashMap。
WeakHashMap其實和HashMap大多數(shù)行為是一樣的,只是WeakHashMap不會阻止GC回收key對象(不是value),那么WeakHashMap是怎么做到的呢,這就是我們研究的主要問題。
在開始WeakHashMap之前,我們先要對弱引用有一定的了解。
在Java中,有四種引用類型
- 強引用(Strong Reference),我們正常編碼時默認(rèn)的引用類型,強應(yīng)用之所以為強,是因為如果一個對象到GC Roots強引用可到達(dá),就可以阻止GC回收該對象
- 軟引用(Soft Reference)阻止GC回收的能力相對弱一些,如果是軟引用可以到達(dá),那么這個對象會停留在內(nèi)存更時間上長一些。當(dāng)內(nèi)存不足時垃圾回收器才會回收這些軟引用可到達(dá)的對象
- 弱引用(WeakReference)無法阻止GC回收,如果一個對象時弱引用可到達(dá),那么在下一個GC回收執(zhí)行時,該對象就會被回收掉。
- 虛引用(Phantom Reference)十分脆弱,它的唯一作用就是當(dāng)其指向的對象被回收之后,自己被加入到引用隊列,用作記錄該引用指向的對象已被銷毀
這其中還有一個概念叫做引用隊列(Reference Queue)
- 一般情況下,一個對象標(biāo)記為垃圾(并不代表回收了)后,會加入到引用隊列。
- 對于虛引用來說,它指向的對象會只有被回收后才會加入引用隊列,所以可以用作記錄該引用指向的對象是否回收。
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的機制來實現(xiàn)不阻止GC回收Key
如何刪除被回收的key數(shù)據(jù)呢
在Javadoc中關(guān)于WeakHashMap有這樣的描述,當(dāng)key不再引用時,其對應(yīng)的key/value也會被移除。
那么是如何移除的呢,這里我們通常有兩種假設(shè)策略
- 當(dāng)對象被回收的時候,進行通知
- WeakHashMap輪詢處理時效的Entry
而WeakHashMap采用的是輪詢的形式,在其put/get/size等方法調(diào)用的時候都會預(yù)先調(diào)用一個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中沒有一個可靠的通知回調(diào),比如大家常說的finalize方法,其實也不是標(biāo)準(zhǔn)的,不同的JVM可以實現(xiàn)不同,甚至是不調(diào)用這個方法。
當(dāng)然除了單純的看源碼,進行合理的驗證是檢驗分析正確的一個重要方法。
這里首先,我們定義一個MyObject類,處理一下finalize方法(在我的測試機上可以正常調(diào)用,僅僅做為輔助驗證手段)
class MyObject(val id: String) : Any() { protected fun finalize() { Log.i("MainActivity", "Object($id) finalize method is called") } }
然后是調(diào)用者的代碼,如下
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 執(zhí)行3此
- 在沒有強制觸發(fā)GC時,WeakHashMap對象size一直會增加
- 手動出發(fā)Force GC,我們會看到MyObject有finalize方法被調(diào)用
- 再次點擊fab空間,然后輸出的WeakHashMap size急劇減少。
- 同樣我們收到在WeakHashMap增加的日志也會輸出
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
到此這篇關(guān)于淺析Java中的WeakHashMap的文章就介紹到這了,更多相關(guān)淺析WeakHashMap內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談圖片上傳利用request.getInputStream()獲取文件流時遇到的問題
下面小編就為大家?guī)硪黄獪\談圖片上傳利用request.getInputStream()獲取文件流時遇到的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11IDEA插件EasyCode及MyBatis最優(yōu)配置步驟詳解
這篇文章主要介紹了IDEA插件EasyCode MyBatis最優(yōu)配置步驟詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12SpringBoot利用ThreadPoolTaskExecutor批量插入百萬級數(shù)據(jù)的具體實現(xiàn)
ThreadPoolTaskExecutor是Spring提供的任務(wù)執(zhí)行器實現(xiàn)之一,允許開發(fā)者配置線程池參數(shù)以適應(yīng)不同的應(yīng)用場景,創(chuàng)建 ThreadPoolTaskExecutor 實例并設(shè)置核心和最大線程數(shù)等屬性可以優(yōu)化性能,本文介紹了SpringBoot利用ThreadPoolTaskExecutor批量插入百萬級數(shù)據(jù)的具體實現(xiàn)2024-12-12mybatis-plus開啟sql打印的三種方式總結(jié)
這篇文章主要給大家介紹了mybatisplus開啟sql打印的三種方式,文章通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的參考價值,需要的朋友可以參考下2023-11-11如何在 Java 中利用 redis 實現(xiàn) LBS 服務(wù)
基于位置的服務(wù),是指通過電信移動運營商的無線電通訊網(wǎng)絡(luò)或外部定位方式,獲取移動終端用戶的位置信息,在GIS平臺的支持下,為用戶提供相應(yīng)服務(wù)的一種增值業(yè)務(wù)。下面我們來一起學(xué)習(xí)一下吧2019-06-06Spring?IOC容器FactoryBean工廠Bean實例
這篇文章主要為大家介紹了Spring?IOC容器FactoryBean工廠Bean實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05