ALL is Well!

敏捷是一条很长的路,摸索着前进着

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  30 随笔 :: 23 文章 :: 71 评论 :: 0 Trackbacks

此问题在项目中被发现,经查看JDK源码(JDK1.6),String类的public String substring(int beginIndex, int endIndex)的实现让我很意外。

想重现这个场景很容易,请看代码。

 1import java.util.ArrayList;
 2import java.util.List;
 3
 4public class LeakTest {
 5    public static void main(Stringargs) {
 6        List<String> handler = new ArrayList<String>();
 7        for(int i = 0; i < 100000; i++{
 8            Huge h = new Huge();
 9            handler.add(h.getSubString(15));
10        }

11    }

12}

13
14class Huge {
15    private String str = new String(new char[100000]);
16    public String getSubString(int begin, int end) {
17        return str.substring(begin, end);
18    }

19}

执行此代码结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

 

问题就出在Huge类的 getSubString 方法,它调用了String类的substring方法。

来让我们看看 substring 类的实现吧,JDK源码如下:

 1    public String substring(int beginIndex, int endIndex) {
 2    if (beginIndex < 0{
 3        throw new StringIndexOutOfBoundsException(beginIndex);
 4    }

 5    if (endIndex > count) {
 6        throw new StringIndexOutOfBoundsException(endIndex);
 7    }

 8    if (beginIndex > endIndex) {
 9        throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
10    }

11    return ((beginIndex == 0&& (endIndex == count)) ? this :
12        new String(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(int offset, int count, char value[]) {
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    public String getSubString(int begin, int end) {
2        return new String(str.substring(begin, end));
3    }

只要再套一个String的构造方法即可。

 

至于为什么,看看JDK源码,一看便知了。这里就不贴出来了。

 

 

唉,以后写代码得多多小心啊。


----2010年08月27日

本文为原创,欢迎转载,转载请注明出处BlogJava
posted on 2010-09-01 12:41 李 明 阅读(1451) 评论(0)  编辑  收藏 所属分类: Java

只有注册用户登录后才能发表评论。


网站导航: