歡迎光臨
每天分享高質量文章

註冊中心 Eureka 原始碼解析 —— StringCache

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 


  • 1. 概述

  • 2. StringCache

  • 3. 使用場景

  • 666. 彩蛋


1. 概述

本文主要分享 Eureka 自己實現的 StringCache

先一起來看下美團點評技術團隊對 String#intern(...) 的分享:

FROM 《深入解析String#intern》「 引言 」 
在 JAVA 語言中有8中基本型別和一種比較特殊的型別 String。這些型別為了使他們在執行過程中速度更快,更節省記憶體,都提供了一種常量池的概念。常量池就類似一個 JAVA 系統級別提供的快取。 
8 種基本型別的常量池都是系統協調的,String 型別的常量池比較特殊。它的主要使用方法有兩種:

  • 直接使用雙引號宣告出來的 String 物件會直接儲存在常量池中

  • 如果不是用雙引號宣告的 String 物件,可以使用String提供的 intern 方法。intern 方法會從字串常量池中查詢當前字串是否存在,若不存在就會將當前字串放入常量池中

  • 字串常量池能帶來速度更快,更節省記憶體的好處

  • 非雙引號宣告的 String 物件,需要使用 String#intern() 方法,將字串儲存到字串常量池。

看起來一切都非常非常非常美好,那為什麼 Eureka 自己實現了 StringCache ?

繼續參見美團點評技術團隊對 String#intern(...) 的分享:

FROM 《深入解析String#intern》「 native 程式碼 」 
JAVA 使用 JNI 呼叫 c++ 實現的 StringTable 的 intern 方法, StringTable的 intern 方法跟 Java 中的 HashMap 的實現是差不多的, 只是不能自動擴容。預設大小是1009

要註意的是,String 的 String Pool 是一個固定大小的 Hashtable,預設值大小長度是 1009,如果放進 String Pool 的 String 非常多,就會造成Hash衝突嚴重,從而導致連結串列會很長,而連結串列長了後直接會造成的影響就是當呼叫String.intern時效能會大幅下降(因為要一個一個找)。

在 JDK6 中 StringTable 是固定的,就是 1009 的長度,所以如果常量池中的字串過多就會導致效率下降很快。在jdk7中,StringTable的長度可以透過一個引數指定:

  • -XX:StringTableSize=99991

  • JDK 自帶的 String Pool 固定大小( 即使可配 ),不支援自動擴容,大量使用 String#intern(…) 後,會導致效能大幅度下降。

  • Eureka 的應用實體( InstanceInfo ) 的 appNameappGroupNamevipAddresssecureVipAddressmetadata 和應用( Application )的 name 等屬性需要使用到 String Pool ,為了在大量的網路通訊序列化反序列的過程中,速度更快,更節省內容。

另外,FastJSON 在 1.124 版本之前也使用 String#intern(...) 方法,最佳化 JSON Key 的速度和空間,但是在大量動態 JSON Key 的場景下,反而會導致效能下降。所以 FastJSON 1.124 修複了該問題。參見如下:

FROM 《深入解析String#intern》「 fastjson 不當使用 」 

  • But ,FastJSON 1.124 版本之前恰好適合 Eureka ,因為 appNameappGroupName 相對不那麼動態。考慮到可能還是有大量的字串存在,因而實現自定義的 StringCache 類,以解決 StringPool 的 HashTable 不支援動態擴容的情況。

OK,下麵我們來看看 Eureka 是如何實現自定義的 StringCache 類。

推薦 Spring Cloud 書籍

  • 請支援正版。下載盜版,等於主動編寫低階 BUG 。

  • 程式猿DD —— 《Spring Cloud微服務實戰》

  • 周立 —— 《Spring Cloud與Docker微服務架構實戰》

  • 兩書齊買,京東包郵。

推薦 Spring Cloud 影片

  • Java 微服務實踐 – Spring Boot

  • Java 微服務實踐 – Spring Cloud

  • Java 微服務實踐 – Spring Boot / Spring Cloud

2. StringCache

com.netflix.discovery.util.StringCache ,字串快取。程式碼如下:

  1public class StringCache {
  2
  3:     public static final int LENGTH_LIMIT = 38;
  4
  5:     private static final StringCache INSTANCE = new StringCache();
  6
  7:     private final ReadWriteLock lock = new ReentrantReadWriteLock();
  8:     private final Map> cache = new WeakHashMap>();
  9:     private final int lengthLimit;
 10
 11:     public StringCache() {
 12:         this(LENGTH_LIMIT);
 13:     }
 14
 15:     public StringCache(int lengthLimit) {
 16:         this.lengthLimit = lengthLimit;
 17:     }
 18
 19:     public String cachedValueOf(final String str) {
 20:         if (str != null && (lengthLimit 0 || str.length() <= lengthLimit)) {
 21:             // Return value from cache if available
 22:             try {
 23:                 lock.readLock().lock();
 24:                 WeakReference ref = cache.get(str);
 25:                 if (ref != null) {
 26:                     return ref.get();
 27:                 }
 28:             } finally {
 29:                 lock.readLock().unlock();
 30:             }
 31
 32:             // Update cache with new content
 33:             try {
 34:                 lock.writeLock().lock();
 35:                 WeakReference ref = cache.get(str);
 36:                 if (ref != null) {
 37:                     return ref.get();
 38:                 }
 39:                 cache.put(str, new WeakReference<>(str));
 40:             } finally {
 41:                 lock.writeLock().unlock();
 42:             }
 43:             return str;
 44:         }
 45:         return str;
 46:     }
 47
 48:     public int size() {
 49:         try {
 50:             lock.readLock().lock();
 51:             return cache.size();
 52:         } finally {
 53:             lock.readLock().unlock();
 54:         }
 55:     }
 56
 57:     public static String intern(String original) {
 58:         return INSTANCE.cachedValueOf(original);
 59:     }
 60
 61: }
  • INSTANCE 屬性,字串快取單例

  • lock 屬性,讀寫鎖,保證讀寫互斥。

  • cache 屬性,快取雜湊表。

    • 使用 WeakHashMap,當 StringCache 被回收時,其對應的值一起被移除。

    • 《WeakHashMap和HashMap的區別》

    • 《Java 集合系列13之 WeakHashMap詳細介紹(原始碼解析)和使用示例》

  • lengthLimit 屬性,快取字串最大長度。預設值:38 。

  • #cachedValueOf(…) 方法,獲得字串快取。若快取不存在,則進行快取。和 String#intern() 的邏輯相同,區別在於 cache 支援自動擴容。

    • 第 22 至 30 行 :讀鎖,讀取快取。

    • 第 32 至 42 行 :快取不存在,寫鎖,寫入快取。

  • #size() 方法,快取大小。

  • #intern() 靜態方法,使用 INSTANCE 獲取快取字串。

3. 使用場景

在 InstanceInfo 下的使用,點選 連結 檢視。

在 Application 下的使用,點選 連結 檢視。

666. 彩蛋

又 Get 新姿勢了,好開森。

胖友,分享個朋友圈,可好?!




如果你對 Dubbo 感興趣,歡迎加入我的知識星球一起交流。

知識星球

目前在知識星球(https://t.zsxq.com/2VbiaEu)更新瞭如下 Dubbo 原始碼解析如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 伺服器

16. P2P 伺服器

17. HTTP 伺服器

18. 序列化 Serialization

19. 叢集容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing


一共 60 篇++

原始碼不易↓↓↓

點贊支援老艿艿↓↓

贊(0)

分享創造快樂