StringBuilder的toString方法

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

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

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

 

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

Java代码   收藏代码
  1. public static void main(String[] args) throws Exception {  
  2.     BufferedReader reader = new BufferedReader(  
  3.                       new FileReader("E:\\java_tools\\hatrix1.28\\hatrix.log"));  
  4.     StringBuilder sb = new StringBuilder();  
  5.     String str = null;  
  6.     while((str=reader.readLine())!=null){  
  7.         if(StringUtils.isNotEmpty(sb.toString())){  
  8.             sb.append("$$$$$$$$\r");  
  9.         }  
  10.         sb.append(str);  
  11.     }  
  12.     System.out.println(sb.toString());  
  13. }  

 

 

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

 

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

 

Java代码   收藏代码
  1.  public String toString() {  
  2.        // Create a copy, don't share the array  
  3. return new String(value, 0, count);  
  4.    }  

 

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

 

 

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

Java代码   收藏代码
  1. public String(char value[], int offset, int count) {  
  2.        if (offset < 0) {  
  3.            throw new StringIndexOutOfBoundsException(offset);  
  4.        }  
  5.        if (count < 0) {  
  6.            throw new StringIndexOutOfBoundsException(count);  
  7.        }  
  8.        // Note: offset or count might be near -1>>>1.  
  9.        if (offset > value.length - count) {  
  10.            throw new StringIndexOutOfBoundsException(offset + count);  
  11.        }  
  12.        this.offset = 0;  
  13.        this.count = count;  
  14.        this.value = Arrays.copyOfRange(value, offset, offset+count);  
  15.    }  
 

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

 

Java代码   收藏代码
  1. public static char[] copyOfRange(char[] original, int from, int to) {  
  2.       int newLength = to - from;  
  3.       if (newLength < 0)  
  4.           throw new IllegalArgumentException(from + " > " + to);  
  5.       char[] copy = new char[newLength];  
  6.       System.arraycopy(original, from, copy, 0,  Math.min(original.length - from, newLength));  
  7.       return copy;  
  8.   }  

 

 

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

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

 

Java代码   收藏代码
  1. /** 
  2.    * The value is used for character storage. 
  3.    */  
  4.   char value[];  
  5.   
  6.   /**  
  7.    * The count is the number of characters used. 
  8.    */  
  9.   int count;   

 

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

 

解决办法:

 

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

你可能感兴趣的:(编程语言)