String是java中经常使用的类,如果使用不当,也有可能出现内存泄露。例如执行以下代码就可能出现内存不够:
public class Test {
private String large = new String(new char[100000]);
public String getSubString() {
return this.large.substring(0, 2);
}
public static void main(String[] args) {
ArrayList<String> subStrings = new ArrayList<String>();
for (int i = 0; i < 1000000; i++) {
Test test = new Test();
subStrings.add(test.getSubString());
}
}
}
我在机器上执行后出现以下错误:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.lang.String.<init>(Unknown Source)
at com.bond.test.Test.<init>(Test.java:6)
at com.bond.test.Test.main(Test.java:17)
这是为什么呢? subStrings 不是保存了1000000个长度为2的字符串吗,一个字符2个字节,总共不过40M 怎么会内存不够呢。其实不是这样的。
我们来看String中的subString方法:
public String substring (int start, int end) {
if (start == 0 && end == count)
return this;
// NOTE last character not copied!
// Fast range check.
if (0 <= start && start <= end && end <= count) {
return new String (offset + start, end - start, value);
}
throw new StringIndexOutOfBoundsException();
}
String (int start, int length, char[] data) {
value = data;
offset = start;
count = length;
}
我们可以看到subString返回的String与原来的String实例共享同一个value,它们引用的同一个char数组。我们可以从下面代码得到答案:
String str1 = "sldkfknkldbjongekgds";
String str2 = str1.substring(3, 10);
Class c1 = str1.getClass();
Field f1 = c1.getDeclaredField("value");
f1.setAccessible(true);
Class c2 = str2.getClass();
Field f2 = c2.getDeclaredField("value");
f2.setAccessible(true);
System.out.println(f1.get(str1)==f2.get(str2));
System.out.println(Arrays.toString(((char[])(f2.get(str1)))));
结果:
true
[s, l, d, k, f, k, n, k, l, d, b, j, o, n, g, e, k, g, d, s]
可见虽然str2的长度为7,但是实际大小却和str1一样。
这个问题的解决办法就是new String(str.subString(int));来替代直接用subString 方法得到String实例:
public String getSubString() {
return new String(this.large.substring(0,2));
}
这样创建的String的实际大小就只有2了。