StringBuilder的toString方法

前几天发布应用出现了点问题,排查过程中同事发在群里一篇文章,仔细看了一下,还是挺有意思的,于是自己模拟了一下场景,

原文在这里,一次简单致命的错误:http://www.blogjava.net/xylz/archive/2012/03/15/371966.html

在频繁的进行字符串的操作增加的时候,我们会优先考虑使用StringBuilderStringBuffer,其中StringBuffer是线程安全的。有个小地方需要慎重,就是toString()方法。

先看这段代码://读取文件,然后在文件的末尾追加一些特殊字符然后换行

	public static void main(String[] args) throws Exception {
		BufferedReader reader = new BufferedReader(
                       new FileReader("E:\\java_tools\\hatrix1.28\\hatrix.log"));
		StringBuilder sb = new StringBuilder();
		String str = null;
		while((str=reader.readLine())!=null){
			if(StringUtils.isNotEmpty(sb.toString())){
				sb.append("$$$$$$$$\r");
			}
			sb.append(str);
		}
		System.out.println(sb.toString());
	}

 

 

上面标红的这里,在小应用或者数据量不大的情况下完全没有问题,但是在数据量大,并且并发多的时候,就会出现问题。

在Stringbuilder的代码中看toString()的代码如下

  public String toString() {
        // Create a copy, don't share the array
	return new String(value, 0, count);
    }

 每次调用toString方法,会重新new一个String出来。

在java.lang.String的代码中,看最后一行,value并不是直接指向的,而是通过系统拷贝函数进行的内存操作

 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);
    }
 

java.util.Arrays中关于数组拷贝

  public static char[] copyOfRange(char[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0)
            throw new IllegalArgumentException(from + " > " + to);
        char[] copy = new char[newLength];
        System.arraycopy(original, from, copy, 0,  Math.min(original.length - from, newLength));
        return copy;
    }

那为啥会通过内存拷贝的形式来进行toString呢,原因在于StringBuilder内部有两个核心的属性,这两个记录了String中的内容,是字符数组的形式。

这时候对于字符数组操作,通过内存拷贝是最快的方式了。

  /**
     * The value is used for character storage.
     */
    char value[];

    /** 
     * The count is the number of characters used.
     */
    int count; 

上述代码中,CPU会一直忙于进行内存的分配工作,会导致机器的load过高

解决办法:

用StringBuilder类中的length来判断是否有数值,这样就避免了无谓的内存操作。

猜你喜欢

转载自iamzhongyong.iteye.com/blog/1470894