关于Java中String的Memory问题

阅读更多

昨天看到一个blog,用一个极端例子来说明Java中的内存问题:

http://blog.xebia.com/2007/10/04/leaking-memory-in-java/

测试代码如下:
  1. public class TestGC {   
  2.   private String large = new String(new char[100000]);   
  3.   
  4.   public String getSubString() {   
  5.     return this.large.substring(0,2);   
  6.   }   
  7.   
  8.   public static void main(String[] args) {   
  9.     ArrayList subStrings = new ArrayList();   
  10.     for (int i = 0; i <1000000; i++) {   
  11.       TestGC testGC = new TestGC();   
  12.       subStrings.add(testGC.getSubString());   
  13.     }   
  14.   }   
  15. }   
  16.   

如果要改掉这个bug,只要改写一下getSubString的方法就好了。

  1. public String getSubString() {   
  2.     return new String(this.large.substring(0,2)); // <-- fixes leak!   
  3.   }   

我测试了一下,确实如此。然后我看了一下JDK1.5.0_06代码中String类

from JDK1.5.0_06, String class:
  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.     }  

这里用到的String的构造函数是private的

  1. private String(int offset, int count, char value[]) {   
  2.     this.value = value;   
  3.     this.offset = offset;   
  4.     this.count = count;   
  5.     }  

上面那个例子程序改写的部分其实就是增加调用public的String构造函数另外创建一个String对象,我们再来看看这个public的构造函数,已经另外一个类似上面这个private的构造函数的public构造函数:

两个public的构造函数
  1. public String(String original) {   
  2.     int size = original.count;   
  3.     char[] originalValue = original.value;   
  4.     char[] v;   
  5.     if (originalValue.length > size) {   
  6.         // The array representing the String is bigger than the new   
  7.         // String itself.  Perhaps this constructor is being called   
  8.         // in order to trim the baggage, so make a copy of the array.   
  9.         v = new char[size];   
  10.         System.arraycopy(originalValue, original.offset, v, 0, size);   
  11.     } else {   
  12.         // The array representing the String is the same   
  13.         // size as the String, so no point in making a copy.   
  14.         v = originalValue;   
  15.     }   
  16.     this.offset = 0;   
  17.     this.count = size;   
  18.     this.value = v;   
  19.     }   
  20.          
  21. public String(char value[], int offset, int count) {   
  22.         if (offset < 0) {   
  23.             throw new StringIndexOutOfBoundsException(offset);   
  24.         }   
  25.         if (count < 0) {   
  26.             throw new StringIndexOutOfBoundsException(count);   
  27.         }   
  28.         // Note: offset or count might be near -1>>>1.   
  29.         if (offset > value.length - count) {   
  30.             throw new StringIndexOutOfBoundsException(offset + count);   
  31.         }   
  32.         char[] v = new char[count];   
  33.         System.arraycopy(value, offset, v, 0, count);   
  34.         this.offset = 0;   
  35.         this.count = count;   
  36.         this.value = v;   
  37.     }  

上面这两个public的构造函数,另外创建出来的String对象和原来的String对象并不共享其char数组的。而前面那个private的构造函数没有作arraycopy,是共享char其数组的。我想JDK这个实现有待商讨,内部使用的private的构造函数中不需要copy数组,可以共享数组。但private的构造函数被public的subString方法调用后,被外部使用时,就有问题了。

 以后写相关代码时要小心了! 

 

 

你可能感兴趣的:(Java,JDK,Blog)