在Java中我们无须关心内存的释放,JVM提供了内存管理机制,有垃圾回收器帮助回收不需要的对象。但实际中一些不当的使用仍然会导致一系列的内存问题,常见的就是内存泄漏和内存溢出
内存溢出(out of memory ):通俗的说就是内存不够用了,比如在一个无限循环中不断创建一个大的对象,很快就会引发内存溢出。
内存泄漏(leak of memory):是指为一个对象分配内存之后,在对象已经不在使用时未及时的释放,导致一直占据内存单元,使实际可用内存减少,就好像内存泄漏了一样。
由substring方法引发的内存泄漏
substring(int beginIndex, int endndex )是String类的一个方法,但是这个方法在JDK6和JDK7中的实现是完全不同的(虽然它们都达到了同样的效果)。了解它们实现细节上的差异,能够更好的帮助你使用它们,因为在JDK1.6中不当使用substring会导致严重的内存泄漏问题。
1、substring的作用
substring(int beginIndex, int endIndex)方法返回一个子字符串,从父字符串的beginIndex开始,结束于endindex-1。父字符串的下标从0开始,子字符串包含beginIndex而不包含endIndex。
String x= "abcdef";
x= str.substring(1,3);
System.out.println(x);
String x= "abcdef"; x= str.substring(1,3); System.out.println(x);上述程序的输出是“bc”
2、实现原理
String类是不可变变,当上述第二句中x被重新赋值的时候,它会指向一个新的字符串对象,就像下面的这幅图所示:
然而,这幅图并没有准确说明的或者代表堆中发生的实际情况,当substring被调用的时候真正发生的才是这两者的差别。
JDK6中的substring实现
String对象被当作一个char数组来存储,在String类中有3个域:char[] value、int offset、int count,分别用来存储真实的字符数组,数组的起始位置,String的字符数。由这3个变量就可以决定一个字符串。当substring方法被调用的时候,它会创建一个新的字符串,但是上述的char数组value仍然会使用原来父数组的那个value。父数组和子数组的唯一差别就是count和offset的值不一样,下面这张图可以很形象的说明上述过程。
看一下JDK6中substring的实现源码:
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); //使用的是和父字符串同一个char数组value
}
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); //使用的是和父字符串同一个char数组value }
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
由此引发的内存泄漏泄漏情况:
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3);
str = null;
String str = "abcdefghijklmnopqrst"; String sub = str.substring(1, 3); str = null;