1.针对常量池的优化
针对常量池的优化是指: 当String对象拥有相同的值时,他们只引用常量池中的同一个拷贝.
public static void main(String[] args) {
String str1 = "abc";
String str2 = "ab" + "c";
String str3 = new String("abc");
System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str1==str3.intern());
}
true
false
true
以上代码显示str1 和str2引用了相同的地址,str3另开辟了新内地址; 参考图1,可以看出str1和str3在常量池中的位置是一样的,intern()方法返回是字符串对象在常量池中的引用.
图1 String内存分配方式
查看String.intern()源码,如下:
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
上面是jdk源码中对intern方法的详细解释。
简单来说就是intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。
否则,在常量池中加入该对象,然后 返回引用。
2.subString()方法的内存泄露(JDK1.6)
subString()在jdk1.6中的源码如下:
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);
}
}
// Package private constructor which shares value array for speed.
//包作用域的构造函数,目的是高效快速的 String 内的 char 数组对象
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
通过上面代码可知,调用subString()
方法,子字符串会复制整个String对象,然后做截取操作; 如果String对象本身比较大,重复做subString()操作,就会占据很大的内存.
虽然提高了运算速率,但是牺牲了内存空间(空间换时间).
再来观察下jdk1.7及以上版本中subString()方法源码:
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
public static char[] copyOfRange(char[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
char[] copy = new char[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
通过native的arraycopy复制了一份指定位置和长度的char数组.