JDK 缓存清单
Abstract:简单整理
Java
中出现的各种缓存
什么东西需要缓存?
缓存的一般都是对象,即在堆上存储的数据,它们具备以下特点:
- 数据量小
- 需要频繁访问读取
缓存的时机
根据不同类型的缓存分类讨论
初始化缓存:基本数据类型缓存池
Java中的基本数据类型的包装类,诸如Integer
、Long
、Short
,均设有对应的缓存池
缓存的时机是包装类被加载进内存时,缓存代码写在对应包装类的static
块中,以Integer
为例
static final int low = -128;
static final int high;
static final Integer cache[];
//缓存了-128 ~ 127 之间的数
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE : 如果缓存池大小指定比127大,就会扩大缓存池
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1]; //new 出缓存
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
其他包装类的默认缓存范围:
(但是注意一下:这个缓存范围并不是固定的,可以通过调整JVM参数 XX:AutoBoxCacheMax = < size > 来修改缓存池的大小,根据JLS7 5.1.7,缓存只能改大不能改小)
- boolean values true and false
- all byte values
- short values between -128 and 127
- int values between -128 and 127
- char in the range \u0000 to \u007F
懒缓存:String Pool
String Pool本质上是常量池,当然也可以认为它是缓存,(另外注意一下:jdk7及以前,String Pool都是在Perm即永久代,而不是堆上,这里不作讨论)
String的不变性支持了String Pool的实现(final修饰符):
- 由于hash值不变,如果缓存中存在“aba”字符串,那么我们可以通过hash直接引用到该对象,而不是重新new
- 另外不变性间接使String变为线程安全的类
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2); // false
String s3 = s1.intern();
String s4 = s1.intern();
System.out.println(s3 == s4); // true
intern()是怎么回事?[1]
-
如果String Pool中已经存在该字符串,那么就直接返回,否则添加进常量池后返回(缓存初始化)
-
根据openjdk,本质上最后是调用了c++中的
StringTable::intern
方法oop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) { unsigned int hashValue = java_lang_String::hash_string(name, len); //计算hash值 int index = the_table()->hash_to_index(hashValue); //计算hash索引 oop string = the_table()->lookup(index, name, len, hashValue); //查找常量池 // Found if (string != NULL) return string; // Otherwise, add to symbol to table return the_table()->basic_add(index, string_or_null, name, len, //如果不存在,则先加入常量池 hashValue, CHECK_NULL); }
-
intern()
的正确使用姿势for (int i = 0; i < MAX; i++) { //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])); arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern(); }
当然,前提是缓存的目标大多都是不变的,如果缓存的String大量变动,就会对String Pool造成巨大的压力