fieldcache講解

爲了支持變長字段和更豐富的數據類型,我們在搜索項目中對lucene的FieldCache做了部分擴展;但使用下來碰到了內存泄漏,所以這裏對 lucene的FieldCache做下細緻的瞭解。FieldCache使用WeakHashMap做核心的cache管理,key是IR對象內部的 frequency文件對象,value是對應字段的數組。雖然WeakHashmap像個核武器,聲稱可以自動釋放對象;爲了分析問題,我們更改了解他 的工作原理,爲了瞭解WeakHashmap,也需要知道WeakReference的工作機制。

1. java的Reference

java把引用(即Reference)分成4種類型,從強到弱分別是:Strong, Soft, Weak, Phantom。這4種不同的reference是java語言規定的內存管理和回收的規範。我們都知道,java的內存,用戶只負責申請,不負責釋放, 由jvm虛擬機通過garbage collection的形式進行回收。內存回收一般是通過mark-sweep的形式進行的,從當前所有線程堆棧和寄存器中的對象往外擴展,mark所有 引用路徑上的對象;結束後,未被mark的對象可以認爲是dead對象,可以回收。這種mark-sweep模式是比較好理解的;但java爲了做朵花出 來,在mark的過程中,支持對對象進行分類(就是這四種reference了),而且每種分類垃圾回收的策略不一樣:strong reference就是我們常用的,不做任何修飾,只要在引用路徑上一定會被mark,防止被gc回收;SoftReference,如果到某個對象的最 後一個引用是soft,那麼這個對象是否被mark,要視heap內存情況而定,空閒較多就mark,內存緊張就不mark讓gc回 收;weakreference是soft的弱化版,如果最後一個引用是weak,gc時一定不會mark;而phantom純粹是搞笑,唯一的用處就在 ReferenceQueue上。

比如說有一個WeakReference的數組或者一個hashmap的key是WeakReference(比如weakhashmap), 怎麼知道哪些WeakReference指向的對象被回收了呢?一種辦法是定期逐個掃描並調用其get方法,返回null就知道被內存回收了 (Phantom用不了,因爲總是返回null),這種方法很明顯低效。ReferenceQueue就是爲了配合這個來使用的,構造 WeakReference時傳遞一個ReferenceQueue對象,這樣當gc要回收它指向的對象時,會把這個reference對象加到 queue中,外部通過檢查這個queue就可以知道哪些對象被回收了。

關於java的reference有一篇經典的介紹可以參考:http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html。寫慣了c/c++,用這種高級的玩意很不習慣。

2. WeakHashmap的實現

順便說一下WeakHashMap的實現機制,key是一個weakreference對象,value是強對象。如果 WeakReference指向的key對象被gc默默回收了,而weakreference作爲hashmap的key還存在,value是強對象也不 會被回收。WeakHashmap使用了ReferenceQueue來解決這個問題,key被gc回收的時候,對應的weakreference對象對 被加入到queue中;當weakhashmap發生方法調用時,在執行真正方法前,會去遍歷下這個queue,把相應的key從hashmap中刪 除,value自然也就沒有引用了,下次gc時就可以被回收了。有一篇文檔講的很清楚,可以參考:http://mikab.iteye.com /blog/587995。

3. lucene的FieldCache實現

知道這些,我們就很容易理解lucene的FieldCache了,他的key是indexreader對象的frequency文件對象,不直接用indexreader,是爲了避免reader之間的淺copy構造,導致一份數據多次cache。解釋見:
  // This is necessary so that cloned SegmentReaders (which
  // share the underlying postings data) will map to the
  // same entry in the FieldCache.  See LUCENE-1579.
利用WeakHashmap的這個特性,當indexreader釋放的時候,FieldCache中這個IR相關的cache都會被釋放;但 當從代碼中,我們可以看到,在添加entry時,lucene還是手動註冊了一個reader closed事件,其中會手動刪除這個reader在hashmap中的entry。按道理說,這兩個是重複的,既然IR close時會把相應的cache都清楚掉,爲什麼還需要WeakHashmap的特性呢,lucene爲什麼這麼做?也是有解釋 的:https://issues.apache.org/jira/browse/LUCENE-2135

參考:

Understanding weak reference:http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

Understanding weak reference 2:http://dinukaroshan.blogspot.com/2012/01/understanding-java-references.html

WeakHashMap的神話:http://mikab.iteye.com/blog/587995

IndexReader.close should forcefully evict entries from FieldCache: https://issues.apache.org/jira/browse/LUCENE-2135

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章