⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Eureka/string-cache/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

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 书籍

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<String, WeakReference<String>> cache = new WeakHashMap<String, WeakReference<String>>();
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<String> 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<String> 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 属性,缓存哈希表。
  • lengthLimit 属性,缓存字符串最大长度。默认值:38 。
  • #cachedValueOf(...) 方法,获得字符串缓存。若缓存不存在,则进行缓存。和 String#intern() 的逻辑相同,区别在于 cache 支持自动扩容。
    • 第 22 至 30 行 :读锁,读取缓存。
    • 第 32 至 42 行 :缓存不存在,写锁,写入缓存。
  • #size() 方法,缓存大小。
  • #intern() 静态方法,使用 INSTANCE 获取缓存字符串。

3. 使用场景

在 InstanceInfo 下的使用,点击 链接 查看。

在 Application 下的使用,点击 链接 查看。

666. 彩蛋

知识星球

又 Get 新姿势了,好开森。

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

文章目录
  1. 1. 1. 概述
  2. 2. 2. StringCache
  3. 3. 3. 使用场景
  4. 4. 666. 彩蛋