1 String
-
使用方法
- 直接使用双引号声明出来的String对象会直接存储在常量池中。
- 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
-
String#intern
- 会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
- 实现
- JAVA 使用 JNI 调用 c++ 实现的 StringTable 的 intern 方法, StringTable的 intern 方法跟 Java 中的 HashMap 的实现是差不多的, 只是不能自动扩容。默认大小是1009
- JVM 参数指定
- -XX:StringTableSize=99991
- image.png
2 案例
public class StringTest2 {
public static void main(String[] args) {
String s = new String("111");
s.intern();
String s2 = "111";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
char[] strs = {'a','b'};
String s5 = new String(strs,0,2);
s5.intern();
String s6 = "ab";
System.out.println(s5 == s6);
}
}
// 输出
false
true
true
-
jdk8解释
-
s s2
-
String s = new String("111"); 首先 把 111 放入常量池
- 堆中新建一个 String 对象, 实例数据 value[] 指向 常量池 111 的value[]
- s 引用 存储 新建的 String 对象引用。
-
s.intern();
- 常量池已经存在 111 s2 存储 常量池 111 引用
-
s == s2 false
-
-
s3,s4
- new String("1") + new String("1");
- 会优化成 s3 = new StringBuilder("1").append("1").toString();
- StringBuilder.toString 方法
- new String(value, 0, count);
- 所以s5 s6 跟 s3,s4 是一样的。
- StringBuilder.toString 方法
-
s5,s6
- String s5 = new String(strs,0,2);
- 新建 String 对象,this.value = Arrays.copyOfRange(value, offset, offset+count);
- s5 存储 堆中 ab String 对象引用
- s5.intern();
- 常量池不存在 ab, 常量池 不需要存储一份对象,直接存储 堆中引用。
- s6 存储 堆中 ab String 对象引用
- s5 == s6 true
- String s5 = new String(strs,0,2);
-
-
可能的问题
- JDK 自带的 String Pool 固定大小,不支持自动扩容
- c++ StringTable 实现 类似java HashMap,
- 散列表 数量太多就会 导致 hash冲突严重,链地址 链变长,性能变差。
3 Eureka StringCache
- 使用场景
- appName 服务名
- appGroupName 组名
- vipAddress 虚拟主机名
- 元数据 key,value 等等。
- image.png
/**
* An alternative to {@link String#intern()} with no capacity constraints.
* String#intern() 的替代选择,没有容量限制。
*
* @author Tomasz Bak
*/
public class StringCache {
public static final int LENGTH_LIMIT = 38;
private static final StringCache INSTANCE = new StringCache();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
//key 不存在 强引用,下次gc 直接回收
private final Map<String, WeakReference<String>> cache = new WeakHashMap<String, WeakReference<String>>();
private final int lengthLimit;
public StringCache() {
this(LENGTH_LIMIT);
}
public StringCache(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;
}
public int size() {
try {
lock.readLock().lock();
return cache.size();
} finally {
lock.readLock().unlock();
}
}
public static String intern(String original) {
return INSTANCE.cachedValueOf(original);
}
}
网友评论