問題定位:
最近奉命解決游戲中一直在的內(nèi)存不夠的問題,最終定位到底層的緩存機制使用的是google提供的ConcurrentLinkedHashMap做數(shù)據(jù)緩存的。
簡單介紹下ConcurrentLinkedHashMap,這是一個基于LRU策略的緩存,說白了就是熱點數(shù)據(jù)緩存,支持設(shè)置最大緩存?zhèn)€數(shù),監(jiān)控到緩存數(shù)量超過最大值后會依據(jù)權(quán)重策略讓數(shù)據(jù)過期。
分析:
根據(jù)ConcurrentLinkedHashMap的特性,我們這邊推測出玩家數(shù)據(jù)在被加載放到緩存后,如果緩存沒有超過設(shè)置的最大值,則這些數(shù)據(jù)會一直放在緩存中;合理上來說這些沒被使用的數(shù)據(jù),在空閑一定時間后是可以被清除的,而不用一直占據(jù)在緩存中。
然而ConcurrentLinkedHashMap并沒有在數(shù)據(jù)空閑一定時間后自動清空數(shù)據(jù)的機制,因而想要解決這個緩存問題,必須使用一個既支持基于緩存?zhèn)€數(shù)大小回收,又支持基于空閑時間回收的第三方組件,而在多番了解和數(shù)據(jù)查證后最終選擇了Caffeine。
Caffeine是什么
Caffeine 是一個高性能的 Java 緩存庫,緩存和 Map 之間的一個根本區(qū)別在于緩存可以根據(jù)回收策略回收存儲的數(shù)據(jù)。此策略直接影響緩存的命中率 ,而緩存命中率額也是緩存庫的一個重要特征。Caffeine 因使用 Window TinyLfu 回收策略,提供了一個近乎最佳的命中率。
我們可以看到官網(wǎng)提供的幾張性能截圖
場景1:8個線程讀,100%的讀操作

場景二:6個線程讀,2個線程寫,也就是75%的讀操作,25%的寫操作

場景三:8個線程寫,100%的寫操作

可以清楚的看到Caffeine效率明顯的高于其他緩存。
將Caffeine替換掉ConcurrentLinkedHashMap,接下來就是要驗證下,是否支持基于空閑時間回收,進而解決我們游戲中的內(nèi)存問題了。
Caffeine和ConcurrentLinkedHashMap的使用對比
操作流程:本地起兩個服,一個緩存底層使用的是ConcurrentLinkedHashMap,另一個底層使用的是Caffeine,Caffeine服設(shè)置的數(shù)據(jù)操作空閑時間是1分鐘,起服后分別使用jvisualvm記錄起始內(nèi)存曲線,截圖保留,之后啟動2000個機器人模擬登陸,登陸后再過1分鐘分別手動調(diào)用FullGC,查看jvisualvm記錄的內(nèi)存曲線,并查看堆dump記錄。
【ConcurrentLinkedHashMap服】起始內(nèi)存

【ConcurrentLinkedHashMap服】2000個機器人模擬登陸后下線,并手動調(diào)用FullGC后查看內(nèi)存曲線

我們可以看到堆內(nèi)存并沒有回到原來的曲線。
查看堆dump記錄

可以看到該對象的實例數(shù)沒有下降到0。
【Caffeine服】起始內(nèi)存

【Caffeine服】2000個機器人模擬登陸后下線,并手動調(diào)用FullGC后查看內(nèi)存曲線

這里看到內(nèi)存曲線回到了起服時的模樣,再查看堆dump記錄

可以看到,對象實例個數(shù)也被回收了,那么這里剩余的實例個數(shù)1是怎么回事,這里涉及到Caffeine的原理,留個懸念,等下篇文章再講解,有興趣關(guān)注一波,后期準(zhǔn)備寫個Caffeine系列教程。
通過以上對比,可以得出Caffeine具備“自動”清空緩存過期數(shù)據(jù)的機制,并且可以解決我們游戲中一直存在的內(nèi)存問題。
總結(jié)
目前對Caffeine的替換已經(jīng)提交到線上運行了,由于目前網(wǎng)上相關(guān)教程偏少,因而后續(xù)準(zhǔn)備寫一系列Caffeine相關(guān)的文章進行解密,有興趣的關(guān)注一波。