前几天性能测试的时候发现一个web 端cpu出现骤降的问题,一直没有找到原因,起初怀疑是tomcat的线程数有关,后来有怀疑是跟数据库的响应时间太长有关系,后来都一一排除了。
之所以此问题比较难以定位主要是因为通过现有的监控工具无法获知和分析tomcat内部各个线程的占用资源的情况。
上周装了一下jprofiler,然后又重新进行了一次压力测试,终于找到了问题的根源:)
主要的资源消耗点是:字符串的拼接上。代码中时使用”+“来进行字符串连接的。
(ps:jprofiler监控的粒度很细,但是由于其本身运行占用的资源消耗量也很大,因此在进行性能测时不能用其作为监控工具,在分析问题方面还是蛮有用的;jconsole是java自带的监控工具,其在远程监控应用程序时,不会对程序的性能造成影响,但由于其监控的粒度还是有些粗,因此tomcat内部的资源占用情况还是无法进行分析的)
JAVA中String ,StringBuffer,SrtingBuilder三个对象连接字符串的效率。
比较下究竟谁的效率高。因为我们经常都听有经验的人说,避免使用String通过“+”连接字符串,特
别是连接的次数很多的时候,一定要用StringBuffer,但究竟效率多高,速度多快,我也没有测试过,
所以我就动手测试下,顺便把测试结果跟大家一起分享,希望大家共同讨论此问题。
下边是我的测试代码,可直接运行:
public class TestStringConnection {
//连接时间的设定
private final static int n = 20000;
public static void main(String[] args){
TestStringConnection test = new TestStringConnection ();
test.testStringTime(n);
test.testStringBufferTime(n);
test.testStringBuilderTime(n);
// //连接10次
// test.testStringTime(10);
// test.testStringBufferTime(10);
// test.testStringBuilderTime(10);
//
// //连接100
//
// test.testStringTime(100);
// test.testStringBufferTime(100);
// test.testStringBuilderTime(100);
//
//
//
// //连接1000
//
// test.testStringTime(1000);
// test.testStringBufferTime(1000);
// test.testStringBuilderTime(1000);
//
//
// //连接5000
//
// test.testStringTime(5000);
// test.testStringBufferTime(5000);
// test.testStringBuilderTime(5000);
//
//
// //连接10000
//
// test.testStringTime(10000);
// test.testStringBufferTime(10000);
// test.testStringBuilderTime(10000);
//
// //连接20000
//
// test.testStringTime(20000);
// test.testStringBufferTime(20000);
// test.testStringBuilderTime(20000);
}
/**
*测试String连接字符串的时间
*/
public void testStringTime(int n){
long start = System.currentTimeMillis();
String a = "";
for(int k=0;k<n;k++ ){
a += "_" + k;
}
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("//连接"+n+"次" );
System.out.println("String time "+n +":"+ time);
//System.out.println("String str:" + str);
}
/**
*测试StringBuffer连接字符串的时间
*/
public void testStringBufferTime(int n){
long start = System.currentTimeMillis();
StringBuffer b = new StringBuffer() ;
for(int k=0;k<n;k++ ){
b.append( "_" + k );
}
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("StringBuffer time "+n +":"+ time);
//System.out.println("StringBuffer str:" + str);
}
/**
*测试StringBuilder连接字符串的时间
*/
public void testStringBuilderTime(int n){
long start = System.currentTimeMillis();
StringBuilder c = new StringBuilder() ;
for(int k=0;k<n;k++ ){
c.append( "_" + k );
}
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("StringBuilder time " +n +":"+ time);
System.out.println("//");
//System.out.println("StringBuffer str:" + str);
}
}
做了个简单统计,得到如下数据:
测试环境:eclipse
|
的情况下,String的低效率表现并不是很突出,但是一旦连接次数多的时候,性能影响是很大的,String进
行2万次字符串的连接,大约需要1分钟时间,而StringBuffer只需要94毫秒,相差接近500倍以上。而
StringBuffer和StringBuilder差别并不大,StringBuilder比StringBuffer稍微快点,我想是因为StringBuffer
是线程序安全的,StringBuilder不是线程序安全的,所以StringBuffer稍微慢点。
但是为什么String如此慢呢,分下如下简单片段
String result="";
result+="ok";
这段代码看上去好像没有什么问题,但是需要指出的是其性能很低,原因是java中的String
类不可变的(immutable),这段代码实际的工作过程会是如何的呢?通过使用javap工具我
们可以知道其实上面的代码在编译成字节码的时候等同的源代码是: String result="";
短短的两个语句怎么呢变成这么多呢?问题的原因就在String类的不可变性上,而java程序为了方便简单的
字符串使用方式对+操作符进行了重载,而这个重载的处理可能因此误导很多对java中String的使用。
需要String,那么使用StringBuffer的toString()方法好了。但是 StringBuilder 的实例用于多个线程是不安
全的。如果需要这样的同步,则建议使用 StringBuffer,因为StringBuffer是线程安全的。在大多数非多
线程的开发中,为了提高效率,可以采用StringBuilder代替StringBuffer,速度更快。
原文地址:http://hi.baidu.com/xinghoney/blog/item/c61ede4c995247ffd62afc2f.html