<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
此前部門內的一個線上系統上線後記憶體一路飆高、一段時間後直接佔滿。協助開發人員去分析定位,發現記憶體中某個Object的量遠遠超出了預期的範圍,很明顯出現記憶體漏失了。
結合程式碼分析發現,洩漏的這個物件,主要存在一個全域性HashMap中,是作為HashMap的Key值。第一反應就是這裡key對應類沒有去覆寫equals()和hashCode()方法,但對照程式碼仔細一看卻發現其實已經按要求提供了自定義的equals和hashCode方法了。進一步走讀業務實現邏輯,才發現了其中的玄機。
鑑於專案程式碼相對保密,這裡舉個簡單的DEMO來輔助說明下。
場景: 記憶體中構建一個HashMap<User, List<Post>>
對映集,用於儲存每個使用者最近的發帖資訊(只是個例子,實際工作中如果遇到這種使用者發帖快取的場景,一般都是用的集中快取,而不是單機快取)。
使用者資訊User類定義如下:
@Data public class User { // 使用者名稱稱 private String userName; // 賬號ID private String accountId; // 使用者上次登入時間,每次登入的時候會自動更新DB對應時間 private long lastLoginTime; // 其他欄位,忽略 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return lastLoginTime == user.lastLoginTime && Objects.equals(userName, user.userName) && Objects.equals(accountId, user.accountId); } @Override public int hashCode() { return Objects.hash(userName, accountId, lastLoginTime); } }
實際使用的時候,使用者發帖之後,會將這個貼文資訊新增到使用者對應的快取中。
/** * 將發帖資訊加入到使用者快取中 * * @param currentUser 當前使用者 * @param postContent 貼文資訊 */ public void addCache(User currentUser, Post postContent) { cache.computeIfAbsent(currentUser, k -> new ArrayList<>()).add(postContent); }
當實際執行的時候,會發現問題就來了,Map中的記錄越來越多,遠超系統內實際的使用者數量。為什麼呢?仔細看下User類就可以知道了!
原來編碼的時候直接用IDE工具自動生成的equals和hashCode方法,裡面將lastLoginTime也納入計算邏輯了。這樣每次使用者重新登入之後,對應hashCode值也就變了,這樣發帖的時候判斷使用者是不存在Map中的,就會再往map中插入一條,隨著時間的推移,記憶體中資料就會越來越多,導致記憶體漏失。
這麼一看,其實問題很簡單。但是實際編碼的時候,很多人往往又會忽略這些細節、或者當時可能沒有這個場景,後面維護的人新增了點邏輯,就會出問題 —— 說白了,就是埋了個坑給後面的人踩上了。
hashCode,即一個Object的雜湊碼。HashCode的作用:
HashCode在上述HashMap等容器中主要是用於尋域,即尋找某個物件在集合中的區域位置,用於提升查詢效率。
一個Object物件往往會存在多個屬性欄位,而選擇什麼屬性來計算hashCode值,具有一定的考驗:
這就與HashMap的底層實現邏輯有關係了。
對於JDK1.8+版本中,HashMap底層的資料結構形如下圖所示,使用陣列+連結串列或者紅黑樹的結構形式:
給定key進行查詢的時候,分為2步:
根據上面的介紹,可以概括為:
這裡也就明白了為什麼hashCode()和equals()需要同時覆寫。
其實,說到這裡,全域性Map出現記憶體漏失,還有一點就是編碼實現的時候缺少對資料退出機制的考慮。 參考下redis之類的依賴記憶體的快取中介軟體,都有一個繞不開的兜底策略,即資料淘汰機制。
對於業務類編碼實現的時候,如果使用Map等容器類來實現全域性快取的時候,應該要結合實際部署情況,確定記憶體中允許的最巨量資料條數,並提供超出指定容量時的處理策略。比如我們可以基於LinkedHashMap來客製化一個基於LRU策略的快取Map,來保證記憶體資料量不會無限制增長,這樣即使程式碼出問題也只是這一個功能點出問題,不至於讓整個程序宕機。
public class FixedLengthLinkedHashMap<K, V> extends LinkedHashMap<K, V> { private static final long serialVersionUID = 1287190405215174569L; private int maxEntries; public FixedLengthLinkedHashMap(int maxEntries, boolean accessOrder) { super(16, 0.75f, accessOrder); this.maxEntries = maxEntries; } /** * 自定義資料淘汰觸發條件,在每次put操作的時候會呼叫此方法來判斷下 */ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxEntries; } }
梳理下幾個要點:
到此這篇關於為什麼不建議使用Java自定義Object作為HashMap的key的文章就介紹到這了,更多相關Java HashMap的key內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45