在压力测试中,通常碰到TPS平稳持续下降的现象,通常为负载机存在资源泄漏。一般有两种:1.连接数,2.JVM内存。本文就第2种类型进行测试和分析。
测试数据:
1. String方式
public int action() throws Throwable {
lr.start_transaction("string");
String a = "The time is "+System.currentTimeMillis();
String b = "The time is "+System.currentTimeMillis();
String c = "The time is "+System.currentTimeMillis();
String d = "The time is "+System.currentTimeMillis();
String e = "The time is "+System.currentTimeMillis();
lr.end_transaction("stringl", lr.AUTO);
写了个简单的给String变量赋值的代码,使用当前时间保证每次赋值都会变化。10个线程执行30分钟。
测试结果:
TPS持续下降:
YGC非常频繁:
同时CPU有走高的趋势
2. StringBuilder方式一(New对象放在Action内)
//将new放在Action中,每次迭代都new一个对象
public int action() throws Throwable {
lr.start_transaction("StringBuilder");
StringBuilder bf1=new StringBuilder();
StringBuilder bf2=new StringBuilder();
StringBuilder bf3=new StringBuilder();
StringBuilder bf4=new StringBuilder();
StringBuilder bf5=new StringBuilder();
bf1.append("The time is ");
bf2.append("The time is ");
bf3.append("The time is ");
bf4.append("The time is ");
bf5.append("The time is ");
bf1.append(System.currentTimeMillis());
bf2.append(System.currentTimeMillis());
bf3.append(System.currentTimeMillis());
bf4.append(System.currentTimeMillis());
bf5.append(System.currentTimeMillis());
bf1.setLength(0);
bf2.setLength(0);
bf3.setLength(0);
bf4.setLength(0);
bf5.setLength(0);
lr.end_transaction("StringBuilder", lr.AUTO);
TPS:
TPS同样会下降
堆内存
3. StringBuilder方式二(New对象放在Action外)
public class Actions
{
StringBuilder bf1=new StringBuilder();
StringBuilder bf2=new StringBuilder();
StringBuilder bf3=new StringBuilder();
StringBuilder bf4=new StringBuilder();
StringBuilder bf5=new StringBuilder();
public int init() throws Throwable {
return 0;
}//end of init
public int action() throws Throwable {
lr.start_transaction("StringBuilder");
bf1.append("The time is ");
bf2.append("The time is ");
bf3.append("The time is ");
bf4.append("The time is ");
bf5.append("The time is ");
bf1.append(System.currentTimeMillis());
bf2.append(System.currentTimeMillis());
bf3.append(System.currentTimeMillis());
bf4.append(System.currentTimeMillis());
bf5.append(System.currentTimeMillis());
bf1.setLength(0);
bf2.setLength(0);
bf3.setLength(0);
bf4.setLength(0);
bf5.setLength(0);
lr.end_transaction("StringBuilder", lr.AUTO);
return 0;
}//end of action
TPS:
TPS有轻微下降,但下降幅度比前两种方式要改善很多。
堆内存
从内存Dump中看到StringBuilder 实例有50个 (10用户,每个用户new 5个对象),但仍然有其他对象占很大比重,不清楚是否是Loadrunner本身所占用。
测试结果:
1. 采用String类型给变量赋值,半小时看到TPS明显下降,并且负载机CPU有上升趋势,剩余内存有下降趋势;
2. 采用StringBuilder给变量赋值,new StringBuilder对象放在Action内(每次迭代均new一次),执行半小时,现象同String类型差不多;
3. 采用StringBuilder给变量赋值,new StringBuilder对象放在Action外,执行半小时,TPS有所下降,但下降趋势比前两个场景要小很多。
原因分析:
1. String 类型和 StringBuffer/StringBuilder 类型的主要性能区别其实在于 String 是不可变的对象,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,(脚本中变量取当前时间,保证每次取值不同) ,new 对象需要消耗资源,并且当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,导致GC频繁;
2. 由于本次压力测试与MAM和BPS有一个共同特点,TPS比较高,因此可能是对象生成速度非常快,GC来不及回收,导致对象不断累积,并且GC越来越频繁导致了CPU上升和TPS逐渐下降,一般应用的TPS较低,因此有足够时间进行回收;
3. 另外,采用Loadrunner Java Vuser方式,LR本身也会生成一些对象,在高TPS下可能会出现对象来不及释放导致性能下降。