1.应用举例
??????? substring方法,例如s="abcdef"? s.substring(2,5)结果为cde,长度为endindex-beginindex
2.实现原理
class="java" name="code">/** * Returns a new string that is a substring of this string. The * substring begins at the specified <code>beginIndex</code> and * extends to the character at index <code>endIndex - 1</code>. * Thus the length of the substring is <code>endIndex-beginIndex</code>. * <p> * Examples: * <blockquote><pre> * "hamburger".substring(4, 8) returns "urge" * "smiles".substring(1, 5) returns "mile" * </pre></blockquote> * * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. * @return the specified substring. * @exception IndexOutOfBoundsException if the * <code>beginIndex</code> is negative, or * <code>endIndex</code> is larger than the length of * this <code>String</code> object, or * <code>beginIndex</code> is larger than * <code>endIndex</code>. */ 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); }
??????? 通过源代码可以发现
??????? a).string内部通过char[]数组实现
??????? b).substring方法返回了新的string对象,但是string对象却指向原来得char数组,如果原来的string很大,即使原来的string释放,内存空间也无法回收
????????c).offset值默认为0,第一个参数为偏移,第二个为长度,第三个为char数组
// Package private constructor which shares value array for speed. String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
??????? d).默认string构造方法如下:
/** * Initializes a newly created {@code String} object so that it represents * an empty character sequence. Note that use of this constructor is * unnecessary since Strings are immutable. */ public String() { this.offset = 0; this.count = 0; this.value = new char[0]; }
??????? e).内存泄露测试
package com.bijian.study.string; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; public class StringTest { public static void main(String[] args) throws UnsupportedEncodingException { String aa=new String(new char[100000]); List<String> list=new ArrayList<String>(); for (int i = 0; i < 10000000; i++) { //虽然截取的字符串占据空间很小,但是由于aa巨大的数组空间被共享,没有释放,所以内存溢出 //list.add(aa.substring(2, 3)); list.add(new String(aa.substring(2,3)));//加一层构造方法后,构造方法内部进行了数组的拷贝,原有的巨大数组空间被回收,不会内存溢出 } } }
??????? 为了避免内存拷贝、加快速度,Sun JDK直接复用了原String对象的char[],偏移量和长度来标识不同的字符串内容。也就是说,substring出的来String小对象仍然会指向原String大对象的char[],split也是同样的情况。这就解释了,为什么HashMap中String对象的char[]都那么大。
原因解释:
??????? a.程序从每个请求中得到一个String大对象,该对象内部char[]的长度达数百K。
??????? b.程序对String大对象做split,将split得到的String小对象放到HashMap中,用作缓存。
??????? c.Sun JDK6对String.split方法做了优化,split出来的Stirng对象直接使用原String对象的char[]
??????? d.HashMap中的每个String对象其实都指向了一个巨大的char[]
??????? e.HashMap的上限是万级的,因此被缓存的Sting对象的总大小=万*百K=G级
??????? f.G级的内存被缓存占用了,大量的内存被浪费,造成内存泄露的迹象
解决方案:
??????? split是要用的,但是我们不要把split出来的String对象直接放到HashMap中,而是调用一下String的拷贝构造函数String(String original),这个构造函数是安全的,源代码如下:
/** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * * @param original * A {@code 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; }?