String 类是非可变类(对象一旦创建了就不能够改变其内在状态了),根据书Effective java中说到,避免创建重复的对象原则。多个引用使用同一个对象而互相不影响。
String S=new String("kill"); 构造函数来创建字符串,如果所创建的字符串在String Pool 中不存在则调用构造函数创建全新的字符串,如果所创建的字符串在字符串缓存池中已有则再拷贝一份到 Java 堆中。总共两个对象。
String s2="kill"; 首先在String Pool池中找(通过equal)有没有kill这个对象,如果有直接把引用指向这个kill对象,如果没有,在heap中创建一个对象(和第一种情况一样),并且把该引用添加到String Pool中。而对第一中情况JVM是不会主动添加到String Pool中去的。
大规模文本分析的案例,实际使用的内存比预估的产生的内存数大的多,主要是在大量使用String.split()
或 String.substring()来操作字符时出现
大量的内存消耗。
JDK 1.6 中 String.substring(int, int)
的源码为:
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
String.substring()
所返回的 String 仍然会保存原始 String。但是JDK String 的源码设计当然有着其合理之处,对于通过 String.split()
或 String.substring()
截取出大量 String 的操作,这种设计在很多时候可以很大程度的节省内存,因为这些 String 都复用了原始 String,只是通过 int 类型的 start, end 等值来标识每一个 String。
因此有关通过String.split()
或 String.substring()
截取 String 的操作的结论如下
对于从大文本中截取少量字符串的应用,String.substring()
将会导致内存的过度浪费。
对于从一般文本中截取一定数量的字符串,截取的字符串长度总和与原始文本长度相差不大,现有的 String.substring()
设计恰好可以共享原始文本从而达到节省内存的目的。
String 优化建议:
在拼接静态字符串时,尽量用 +。
String test = "this " + "is " + "a " + "test " + "string";
在拼接动态字符串时,尽量用 StringBuffer
或 StringBuilder
的 append。
使用缓存池的技术来解决string() 带来的内存性能隐患
自己构建缓存,这种方式的优点是更加灵活。创建 HashMap,将需缓存的 String 作为 key 和 value 存放入 HashMap。假设我们准备创建的字符串为 key,将 Map cacheMap 作为缓冲池,那么返回 key 的代码如下:
private String getCacheWord(String key) {
String tmp = cacheMap.get(key);
if(tmp != null) {
return tmp;
} else {
cacheMap.put(key, key);
return key;
}
}