图片2
在图2中,你会注意到性能下降了。虽然我使用了更多的CPU,但吞吐量和失败数都更差了。虽然垃圾回收周期变了,但每秒依然需要回收100M。显然我们还没有找到主要的瓶颈。
非竟争的同步相对于简单的函数调用还是很费时的。竟争性的同步就更费时了,因为除了内存需要同步外,VM还需要维护等待的线程。在这种状况下,这些代价实际上要小于内存瓶颈。实际上,通过消除了同步瓶颈,VM内存系统承担了更多的压力最后导致更差的吞吐量,即使我使用了更多的CPU。显然最好的方式是从最大的瓶颈开始,但有时这也不是很容易确定的。当然,确保VM的内存处理足够正常也是一个好的开始方向。
内存瓶颈
现在我会首先也定位内存问题。列表3是GrinderServlet的重构版本,使用了StringBuffer实例。图3显示了测试结果。
Listing 3
package pub.capart;
/**
* This is a simple class designed to simulate an application consuming
* CPU, memory, and contending for a synchronization lock.
*/
public class Grinderv2 {
private static Grinderv2 singleton = new Grinderv2();
private static final String randstr =
“this is just a random string that I'm going to add up many many times”;
private StringBuffer sbuf = new StringBuffer();
private StringBuffer sbufrev = new StringBuffer();
public static Grinderv2 getGrinder() {
return singleton;
}
public synchronized void grindCPU(int level) {
sbufrev.setLength(0);
sbufrev.append(randstr);
sbuf.setLength(0);
for (int i=0;i<level;++i) {
sbuf.append(sbufrev);
reverse();
}
return sbuf.toString();
}
public String getReverse(String s) {
StringBuffer sb = new StringBuffer(s);
sb = sb.reverse();
return sb.toString();
}
}
图片1
通常重用StringBuffer并不是一个好主意,但这里我只是为了重现一些常见的问题,而不量提供解决方案。内存数据已经从图上消失了因为测试中没有垃圾回收器运行。吞吐量戏剧性的增加而CPU使用率又回到了50%。列表3不只是优化了内存,但我认为主要了改善了过度的内存消耗。
文章来源于领测软件测试网 https://www.ltesting.net/