Q:JDK6中的substring()方法容易导致内存泄露而JDK7不会出现这样的情况的原因

@文章来源:https://www.programcreek.com/2013/09/the-substring-method-in-jdk-6-and-jdk-7/

JDK6JDK7中提供的substring(int beginIndex,int endIndex)方法的实现是不同的。这篇文章用来讲解其不同之处。为了简单起见,在这文章中,substring()方法代表了substring(int beginIndex,int endIndex)方法。

 

1.substring方法的作用

substring(int beginIndex,int endIndex)方法返回一个字符串从beginIndex开始到endIndex-1结束的子字符串

String x=”abcdef”;

x=x.substring(1,3);

System.out.println(x);

 

运行结果:

bc

 

 

2.substring()方法内部执行的细节

你可能会知道,因为变量x为不可变的对象,当xx.substring(1,3)重新赋值的时候,其会指向堆内存中的一个新的字符串对象,就像如下图一般:

 

Q:JDK6中的substring()方法容易导致内存泄露而JDK7不会出现这样的情况的原因


然而,这张图表示的并不完全正确。正确的是,在JDK6JDK7substring()方法的实现是不同的。

 

3.substring()方法在JDK6中的实现

字符串对象的实现是一个字符数组。在JDK6中,String类包含了3个成员变量:char[] value,int offset,int count。他们分别被用来存储真实的字符数组,字符串在字符数组中的第一个字符的索引,字符串中字符的数目。

substring()方法被调用的时候,其会新创建一个String对象并返回,但是其新创建的String对象的字符数组value的指针仍指向原String对象的value数组的对象。两个String对象的不同之处仅在于成员变量countoffset的值。

 Q:JDK6中的substring()方法容易导致内存泄露而JDK7不会出现这样的情况的原因

 


 

如下代码仅包含解释该问题的关键部分

//JDK6

String(int offset,int count,char[] value)

{

this.value=value;

this.offset=offset;

this.count=count;

}

public String substring(int beginIndex,int endIndex)

{

return new String(offset+beginIndex,endIndex-beginIndex,value);
}

 

4.JDK6substring()方法存在的问题

如果你有一个非常长的字符串,但你每次使用substring()方法去提取你所需的那很小的一部分字符串,由于你每次仅需要很少的一部分字符串,由于其在堆内存中保存下了一整个数组,为此其会出现性能上的问题(内存泄露)。对于JDK6,解决这个问题的方法是使用如下的方式去提取子串,其会返回一个真正的子串对象:

x=x.substring(beginIndex,endIndex)+””;

/*

*以上代码的实现等价于x=(new StringBuilder()).append(x.substring(1, 3)).append("").toString();

*

*其原因可以参见StringBuilder中对于字符串中append方法的实现,由于在append方法中

*调用了System.arraycopy()方法,为此,其会返回一个真正的字符串

*

*/

5.substring()方法在JDK7中的实现

JDK7中,substring()方法进行了改进,在JDK7substring()方法会在堆内存中创建一个真正的(字符)数组


Q:JDK6中的substring()方法容易导致内存泄露而JDK7不会出现这样的情况的原因


//JDK7

public String(char[] value,int offset,int count)

{

this.value=Arrays.copyOfRange(value,offset,offset+count);

}

public String substring(int beginIndex,int endIndex)

{

int subLen=endIndex-beginIndex;

return new String(value,beginIndex,subLen);

}