1. 概述
2. StringCache
3. 使用场景
RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
您对于源码的疑问每条留言都将得到认真回复. 甚至不知道如何读源码也可以请教噢.
新的源码解析文章实时收到通知. 每周更新一篇左右.
认真的源码交流微信群.
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 微服务架构实战》
两书齐买, 京东包邮.
2. StringCache
com.netflix.discovery.util.StringCache
, 字符串缓存. 代码如下:
public
class StringCache
{
public
static
final
int LENGTH_LIMIT =8;
privatestaticfinal StringCache INSTANCE =
new StringCache();
privatefinal ReadWriteLock lock =
new ReentrantReadWriteLock();
privatefinal Map<String, WeakReference<String>> cache =new WeakHashMap<String, WeakReference<String>>();
privatefinal int lengthLimit;
public StringCache()
{
this(LENGTH_LIMIT);
}
publicStringCache(int lengthLimit)
{
this.lengthLimit = lengthLimit;
}
public String cachedValueOf (final String str)
{
if (str !=null && (lengthLimit <0 || str.length() <= lengthLimit)) {
// Return value from cache if available
try {
lock.readLock().lock();
WeakReference<String> ref = cache.get(str);
if (ref != null) {
return ref.get();
}
} finally {
lock.readLock().unlock();
}
// Update cache with new content
try {
lock.writeLock().lock();
WeakReference<String> ref = cache.get(str);
if (ref != null) {
return ref.get();
}
cache.put(str, new WeakReference<>(str));
} finally {
lock.writeLock().unlock();
}
return str;
}
return str;
}
publicint size()
{
try {
lock.readLock().lock();
return cache.size();
} finally {
lock.readLock().unlock();
}
}
publicstatic String intern(String original)
{
return INSTANCE.cachedValueOf(original);
}
}
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 下的使用, 点击 链接 查看.
来源: http://t.cn/R8yHRE0