此问题在项目中被发现,经查看JDK源码(JDK1.6),String类的public String substring(int beginIndex, int endIndex)的实现让我很意外。
想重现这个场景很容易,请看代码。
1
importjava.util.ArrayList;2
importjava.util.List;3
4
publicclassLeakTest
{5
publicstaticvoidmain(String
args)
{6
Listhandler=newArrayList();7
for(inti=0; i<100000; i++)
{8
Huge h=newHuge();9
handler.add(h.getSubString(1,5));10
}11
}12
}13
14
classHuge
{15
privateString str=newString(newchar[100000]);16
publicString getSubString(intbegin,intend)
{17
returnstr.substring(begin, end);18
}19
}
执行此代码结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
问题就出在Huge类的 getSubString 方法,它调用了String类的substring方法。
来让我们看看 substring 类的实现吧,JDK源码如下:
1
publicString substring(intbeginIndex,intendIndex)
{2
if(beginIndex<0)
{3
thrownewStringIndexOutOfBoundsException(beginIndex);4
}5
if(endIndex>count)
{6
thrownewStringIndexOutOfBoundsException(endIndex);7
}8
if(beginIndex>endIndex)
{9
thrownewStringIndexOutOfBoundsException(endIndex-beginIndex);10
}11
return((beginIndex==0)&&(endIndex==count))?this:12
newString(offset+beginIndex, endIndex-beginIndex, value);13
}
再让我们接下来看看 new String(offset + beginIndex, endIndex - beginIndex, value); 的实现:
1
//Package private constructor which shares value array for speed.2
String(intoffset,intcount,charvalue[])
{3
this.value=value;4
this.offset=offset;5
this.count=count;6
}
char[] value 数组被共享了。
在我们的main函数里的循环中,每循环一次后,我们希望Huge对象被回收,且释放它占有的内存。
但实际上 private String str = new String(new char[100000]); 占有的内存并不会被释放。
因为 我们通过 Huge 类的 getSubString 方法得到的 String 对象还存在(存在于handler的列表中),
它虽然是 length 只有 4 的对象,却享有着 char[100000] 的空间。
解决方案:
可以修改Huge 类的 getSubString 方法如下:
1
publicString getSubString(intbegin,intend)
{2
returnnewString(str.substring(begin, end));3
}
只要再套一个String的构造方法即可。
至于为什么,看看JDK源码,一看便知了。这里就不贴出来了。
唉,以后写代码得多多小心啊。
----2010年08月27日
本文为原创,欢迎转载,转载请注明出处BlogJava。