String中substring方法内存泄漏问题

来源:互联网 发布:spss数据输入 编辑:程序博客网 时间:2024/06/09 14:30

众所周知,JDK中以前String类中的substring方法存在内存泄漏问题,JDK1.7及以后的版本已经修复了。之所以存在内存泄漏的问题,是因为原先的版本中,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);     }  

而其中用到的String构造方法是这样的:

   String(int offset, int count, char value[]) {          this.value = value;          this.offset = offset;          this.count = count;     }  

this.value=value这种实现就出现问题了,因为String类中有几个私有的成员变量:

    /** The value is used for character storage. */     private final char value[];     /** The offset is the first index of the storage that is used. */     private final int offset;     /** The count is the number of characters in the String. */     private final int count;     /** Cache the hash code for the string */     private int hash; // Default to 0  

这种实现还在引用着原先字符串变量的value[],通过offset和count返回一个长得像的“截取”后的字符串给人一种错觉,导致JVM认为这个最初字符串还在被引用着不对其gc,造成了效率问题,如注释所说:
Package private constructor which shares value array for speed.
这样导致的后果就是如果有一个很大很长的字符串我只需要其中的一小部分字符串用substring实现的话,如果让你看似得到的“新”的短小字符串一直没被JVM 回收的话,那么相当这个最初的大字符串也没被回收,尤其是你把这个短小“新”的字符串直接以引用的形式付给一个静态的全局变量,在加上如果访问数量很大,那应该“代价”还是蛮可观的,不过可以简单的这样new(s.substring())就避免了这个问题。
新的JDK中substring之所以不存在这个问题了,是因为这个构造方法改成这样了

   public String(char value[], int offset, int count) {        if (offset < 0) {            throw new StringIndexOutOfBoundsException(offset);        }        if (count < 0) {            throw new StringIndexOutOfBoundsException(count);        }        // Note: offset or count might be near -1>>>1.        if (offset > value.length - count) {            throw new StringIndexOutOfBoundsException(offset + count);        }        this.offset = 0;        this.count = count;        this.value = Arrays.copyOfRange(value, offset, offset+count);    }