點選上方“芋道原始碼”,選擇“置頂公眾號”
技術文章第一時間送達!
原始碼精品專欄
-
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 ) 的
appName
、appGroupName
、vipAddress
、secureVipAddress
、metadata
和應用( 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 ,因為
appName
、appGroupName
相對不那麼動態。考慮到可能還是有大量的字串存在,因而實現自定義的 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
,字串快取。程式碼如下:
1: public 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 篇++
原始碼不易↓↓↓↓↓
點贊支援老艿艿↓↓