### 1 奇怪的現象
在使用Hazelcast的Eviction時,發現觀察到的現象與想象的不同。按照官方文檔介紹,Eviction有這樣幾個配置選項:
~~~
<hazelcast>
<map name="default">
?? ...
?? <time-to-live-seconds>0</time-to-live-seconds>
?? <max-idle-seconds>0</max-idle-seconds>
?? <eviction-policy>LRU</eviction-policy>
?? <max-size policy="PER_NODE">5000</max-size>
?? <eviction-percentage>25</eviction-percentage>
?? ...
</map>
</hazelcast>
~~~
看后三項,按照參數的描述應為:當每個結點的entry數達到5000時,使用LRU策略,剔除25%的entry,即1250個。可是觀察到的現象一般是entry數達到4200左右就止步不前,繼續大量高并發的insert測試,既不會增長也不會減少。這是怎么回事?而且相比Redis,Hazelcast在達到eviction臨界條件后繼續并發插入和讀寫時,性能表現依舊良好,就像沒有發生eviction一樣。是使用了多線程還是什么神奇的算法?源碼之前,了無秘密,還是從代碼中尋找答案吧。
### 2 代碼剖析
我們客戶端使用的,也是最頂層的API,就是IMap了,這里以比put()更為高效的set()作為分析的起點。后面其實能夠看到,因為使用了命令模式(command),兩者都是繼承一個父類,evict都是在一個地方觸發的。
### 2.1 MapProxyImpl包裝器
IMap的直接實現類是MapProxyImpl,但它只是個Wrapper,負責轉換key和value。

### 2.2 MapProxySupport封裝命令對象
真正的實現都是在它繼承的MapProxySupport類中,例如set()調用的setInternal()就能在Support類中找到。各種internal方法將操作包裝成Operation類,這方便了遠程調用的實現,例如set()要執行的結點不在本地。

### 2.3 SetOperation命令模式
invokeOperation()中會確定操作應該在哪個分區執行,這個分區位于哪個結點上。這里Hazelcast維護了一個線程池,每個分區都有對應的線程去執行本分區的操作。因為過程比較復雜,所以這里直接略過,繼續關注我們重點想知道的eviction過程的實現。那么直接看一下SetOperation中的邏輯。SetOperation很簡單,直接調用RecordStore保存鍵值對,但afterRun()中有一些隱含的后處理。

### 2.4 BasePutOperation觸發eviction
果然,在afterRun()中除了廣播事件、使Near緩存失效外,還有觸發eviction過程。Evict()調用的就是RecordStore的evictEntries()方法。

### 2.5 AbstractEvictableRecordStore控制eviction
真正的evict控制邏輯就在這里。首先,shouldEvict()會判斷是否滿足了我們之前配置的eviction的觸發條件,如PER_NODE=5000。如果滿足則調用removeEvictableRecords()開始剔除數據。

### 2.6 EvictionOperator事有蹊蹺
最終removeEvictableRecords調用的是EvictionOperator,具體的實現都在這里。但仔細看這段代碼卻看不出有什么高明之處,只是簡單地迭代RecordStore的記錄,將滿足條件的entry剔除掉。既沒用多線程,也沒什么特殊的算法,這到底是怎么回事?

### 2.7?答案揭曉
謎底其實就在Operation類對應的RecordStore初始化上。我們知道,默認情況下,Hazelcast將map分為271個partition。其實RecordStore也是按這些partition劃分的,而不是使用一個大的RecordStore。所以從BasePutOperation的evict()到后續處理的都只是當前key對應分區的RecordStore。也就是說:當key要被處理時,Eviction發生在對應的partition里,而不會evict所有數據的25%(Redis就是處理database中的所有數據,所以延遲會有所增加)。所以,當我們繼續壓力測試時,不斷有key繼續插入,這些分區就會不斷發生eviction,導致整體的內存使用會保持不變。

### 3 類圖全貌
梳理了上面的執行流程后,我們最后整理一下這些類之間的關系。
