JDK6和JDK7中String的substring(int,int)方法的区别

本文详细对比了JDK6和JDK7下String的substring(int,int)方法的内存管理差异,并通过代码示例解释了在JDK6中如何避免由于共享value导致的内存泄露问题。同时,提供了在JDK6环境下通过重新创建String实例来解决此问题的方法。

我们经常像下面这样使用String的substring(int,int)方法来截取字符串。

public static void main(String... args) {
		String str = "abcdefg";
		str = str.substring(1, 2);
		// do something with str
	}

下面来比较下JDK6和JDK7下String的substring(int,int)方法的源码。

JDK6中的源码

    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(int offset, int count, char value[]) {
        this.value = value;
	    this.offset = offset;
	    this.count = count;
    }

这里在调用new String(int, int, char[])创建一个新字符串时会共享同一个value,改变的仅仅是offset和count;旧的char数组并没有被截取且依然被新的字符串引用,所以就导致了内存泄露。

JDK7中的源码:

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
    
    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.value = Arrays.copyOfRange(value, offset, offset+count);
    }

这里在调用new String(char[], int, int)创建一个新字符串时会调用Arrays.copyOfRange(char[], int, int)方法,而Arrays的copyOfRange方法会使用System.arraycopy(Object,int ,Object,int,int)来截取和复制value,此时旧的value不再被引用,所以不会造成内存泄露。

在ProgramCreek上有网友给出了JDK6下该问题的解决方案(http://www.programcreek.com/2013/09/the-substring-method-in-jdk-6-and-jdk-7/ ):

public static void main(String... args) {
		String str = "abcdefg";
		str = new String(str.substring(1, 2));
		// do something with str
	}

这样在JDK6下就会重新截取并复制一份value了,原因请参考JDK6下String构造器String(String)的源码:

public String(String original) {
    int size = original.count;
    char[] originalValue = original.value;
    char[] v;
    if (originalValue.length > size) {
        // The array representing the String is bigger than the new
        // String itself.  Perhaps this constructor is being called
        // in order to trim the baggage, so make a copy of the array.
        int off = original.offset;
        v = Arrays.copyOfRange(originalValue, off, off+size);
    } else {
        // The array representing the String is the same
        // size as the String, so no point in making a copy.
        v = originalValue;
    }
    this.offset = 0;
    this.count = size;
    this.value = v;
}

此时original也会通过Arrays.copyOfRange(char[], int, int)来截取和复制value,于是便避免了内存泄露。


转载于:https://my.oschina.net/xionghui/blog/313229

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值