性能测试的目标

性能测试不同于功能测试,不是对与错的检验,而是快与慢的衡量。在进行真正的性能测试之前要先搞清楚目标:

1. 在确定的硬件条件下,可以支持的并发数越大越好,响应时间越快越好。具体需要达到的并发数是多大,要求的响应时间是多快,由产品经理来提出。

2. 在确定的硬件条件下,测试得到最大并发数和相应的响应时间之后。如果增加硬件投入,可以得到怎样的性能提升回报? (系统扩展性和伸缩性测试,Scalability)

这里的硬件条件包括:cpu,memery,I/O,network bandwidth。

性能测试中的基准测试 Benchmarking

与功能测试相似,性能测试也要设计测试用例,不同的是在正式开始你的业务测试用例之前你要先进行一下基准测试。为什么呢?其实就是先要量一下你的硬件的能力,不然,如果你的测试结果不好,你怎么知道是硬件慢还是你的软件的问题。这些硬件测试包括:

1. 网络带宽测试, 你可以通过copy大文件的方式测试你的网络的最大带宽是多少。

2. cpu,你可以利用比较复杂的算法来衡量cpu的快慢

3. memery,这个不用测试,你知道memery的大小

4. IO, 也可以通过copy大文件来测试

这些基准测试用例在后面的调优过程中,还可以用来衡量你修改之后真的变好了吗。

设计你的业务测试用例

比较理想的测试用例就是要尽可能模仿真实世界的情况,这往往做不到,尤其是对于新产品来说。你可以先录制一些用户最常用,最典型的case作为起点。

另外,对于并发的概念需要搞清楚。并发用户,通常是指同时在线的用户,这些用户可以能在用你的系统的不同的功能,注意并不是说大家都在做同一件事情。对某一个事务并发请求是指某一个request的并发调用。

对于后一种并发,你往往需要计算在用户量最大的时候,大概大家都集中的在干哪一件事情,这个请求一定要够快才好。

设计好这两种测试用例以后,在后面的调优过程中,他们就成了衡量你的改进的成效的衡量的标尺。

性能调优

性能调优要从底层开始,基本上要从OS开始,到JVM,Cache,Buffer Pool, SQL,DB Schema, 算法。

一次不要改的太多,改一点,测一下,这可是个慢功夫,需要有耐心。

在执行测试的时候还要注意,要遵循相同的过程,系统需要在重启之后先热身再开始真正的测试,不然你会发现你的测试结果很不一样,琢磨不定。

还有,要注意你的客户端的能力,比如JMeter,很需要内存,别因为客户端不行,误以为是你的系统的问题,那就太乌龙了。

在测试调优的时候,需要借助一些监控工具比如JConsole,来监控系统的状况,找到系统的瓶颈,所谓瓶颈,就是最慢的那个部分,也常表现为100%被占满。比如你的内存或者cpu被用尽了。如果cpu和内存还没有用尽,说明他们在等某个资源。这时候需要用profile工具去寻找,比如JProfile,YourKit。

利用性能监控日志

因为性能的问题不是很容易重现,当product环境中遇到性能问题的时候,如果是数据的问题,也许当你把product 数据copy到你的测试环境中,就能重现比较慢点查询,加以改进。但是如果是并发用户或者网络等运行时环境的问题,你就很难重现。这时,如果你能通过日志看到那些关键的响应慢的方法,也许可以帮助你快点找到问题所在。下面的代码可以帮你做到这一点,仅供参考:

  1. import org.slf4j.Logger;
  2. public class TraceUtil {
  3. final Logger logger;
  4. final long threshold = 1000;
  5. private long begin;
  6. private long offtime = 0;
  7. private String threadInfo;
  8. private String targetId;
  9. public TraceUtil(Logger logger, Thread thread, String targetId, long begin) {
  10. this.logger = logger;
  11. this.threadInfo = thread.getId() + "-" + thread.toString();
  12. this.targetId = targetId;
  13. this.begin = begin;
  14. }
  15. public void trace(String targetEvent) {
  16. long duration = System.currentTimeMillis() - begin;
  17. long increment = duration - offtime;
  18. offtime = duration;
  19. float percentage = (float) increment / (float) duration * 100;
  20. if (duration > threshold && percentage > 20) {
  21. logger.error(
  22. "Response time is too large: [{}], {}/{} ({}), {}, {}",
  23. new String[] { threadInfo + "", increment + "",
  24. duration + "", percentage + "%", targetEvent,
  25. targetId });
  26. }
  27. }
  28. }

利用JVM的MXBean找到blocked的点

当你发现JVM占用的cpu很高,而且响应时间比较慢,很可能是被IO或者网络等慢速设备拖住了。也有可能是你的方法中某个同步点(同步方法或者对象)成为性能的瓶颈。这时候你可以利用JVM提供的monitor API来监控:

  1. <%@ page import="java.lang.management.*, java.util.*" %>
  2. <%!
  3. Map cpuTimes = new HashMap();
  4. Map cpuTimeFetch = new HashMap();
  5. %>
  6. <%
  7. out.println("Threads Monitoring");
  8. long cpus = Runtime.getRuntime().availableProcessors();
  9. ThreadMXBean threads = ManagementFactory.getThreadMXBean();
  10. threads.setThreadContentionMonitoringEnabled(true);
  11. long now = System.currentTimeMillis();
  12. ThreadInfo[] t = threads.dumpAllThreads(false, false);
  13. for (int i = 0; i < t.length; i++) {
  14. long id = t[i].getThreadId();
  15. Long idObj = new Long(id);
  16. long current = 0;
  17. if (cpuTimes.get(idObj) != null) {
  18. long prev = ((Long) cpuTimes.get(idObj)).longValue();
  19. current = threads.getThreadCpuTime(t[i].getThreadId());
  20. long catchTime = ((Long) cpuTimeFetch.get(idObj)).longValue();
  21. double percent = (double)(current - prev) / (double)((now - catchTime) * cpus * 1000);
  22. if (percent > 0 && prev > 0) {
  23. out.println("<li>" + t[i].getThreadName()+"#"+t[i].getThreadId() + " Time: " + percent + " (" + prev + ", " + current + ") ");
  24. String locked = t[i].getLockInfo()==null?"":t[i].getLockInfo().getClassName();
  25. out.println(" Blocked: (" + t[i].getBlockedTime() + ", " + t[i].getBlockedCount() + ", " + locked + ")</li>");
  26. }
  27. }
  28. cpuTimes.put(idObj, new Long(current));
  29. cpuTimeFetch.put(idObj, new Long(now));
  30. }
  31. %>

同步是性能的一大瓶颈

通过监控发现,大量线程block在一个同步方法上,这样cpu也使不上劲。当你发现性能上不去,IO和网络等慢速设备也不是问题的时候,你就得检查一下是否在某个关键点上使用了同步(synchronizae)。有时候也许是你应用的第三方的jar里面的某个方法是同步的,这种情况下,你就很难找到问题所在。只能在编写代码的时候看一下你引用的方法是否是同步的。

参考阅读

1. Java run-time monitoring 系列文章,比较系统的讲解了jvm的监控

2. Performance Tuning the JVM for Running Tomcat:本文列举了tomcat性能相关的几个关键的jvm 参数

3. 一本系统讲解Java性能的书:Java Performance

4. insideApps,一个事务级别的JavaEE的性能监控开源软件。它希望可以寄存在product环境中,在不影响系统性能的前提下,监控和分析产品的性能。想法很不错。

5. ManageEngine, 一个很强大的监控软件,有免费版。

原文链接:http://ctomentoring.blog.51cto.com/4445779/794813

如何进行Java EE性能测试与调优的更多相关文章

  1. [java] JVM监控与调优

    原文出处:http://www.cnblogs.com/zhguang/p/java-jvm-gc.html   光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分 ...

  2. jvm系列(六):Java服务GC参数调优案例

    本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响. 这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题. ...

  3. 一文读懂Java GC原理和调优

    概述 本文介绍GC基础原理和理论,GC调优方法思路和方法,基于Hotspot jdk1.8,学习之后将了解如何对生产系统出现的GC问题进行排查解决 阅读时长约30分钟,内容主要如下: GC基础原理,涉 ...

  4. JAVA性能监控与调优参考文档链接

    JAVA性能监控与调优参考文档链接 jdk8工具集 https://docs.oracle.com/javase/8/docs/technotes/tools/unix/index.htmlTroub ...

  5. jvm 命令使用调优 通过jstat、jmap对java程序进行性能调优

    转载:http://blog.csdn.net/jerry024/article/details/8507589 转载: https://blog.csdn.net/zhaozheng7758/art ...

  6. 一文了解MySQL性能测试及调优中的死锁处理方法,你还看不明白?

    一文了解MySQL性能测试及调优中的死锁处理方法,你还看不明白? 以下从死锁检测.死锁避免.死锁解决3个方面来探讨如何对MySQL死锁问题进行性能调优. 死锁检测 通过SQL语句查询锁表相关信息: ( ...

  7. java大数据量调优

    从总体上来看,对于大型网站,比如门户网站,在面对大量用户访问.高并发请求方面,基本的解决方案集中在这样几个环节:1.首先需要解决网络带宽和Web请求的高并发,需要合理的加大服务器和带宽的投入,并且需要 ...

  8. 【java虚拟机】jvm调优原则

    转自:https://www.cnblogs.com/xiaopaipai/p/10522794.html 合理规划jvm性能调优 JVM性能调优涉及到方方面面的取舍,往往是牵一发而动全身,需要全盘考 ...

  9. java 内存 垃圾回收调优

    要了解Java垃圾收集机制,先理解JVM内存模式是非常重要的.今天我们将会了解JVM内存的各个部分.如何监控以及垃圾收集调优. Java(JVM)内存模型 正如你从上面的图片看到的,JVM内存被分成多 ...

随机推荐

  1. ngx_http_stub_status_module

    ngx_http_stub_status_module是一个Nginx的内置 HTTP模块,该模块可以提供Nginx的状态信息.编译的时候 需指定加载该模块: --with-http_stub_sta ...

  2. 行为类模式(五):中介者(Mediator)

    定义 定义一个中介对象来封装系列对象之间的交互.中介者使各个对象不需要显示地相互引用,从而使其耦合性松散,而且可以独立地改变他们之间的交互. 试想一下,如果多个类之间相互都有引用,那么当其中一个类修改 ...

  3. adb无线网络调试

    1.如果已经可以用usb连接adb,那么可以通过以下命令切换到无线连接方式. adb tcpip 5555 adb connect 192.168.0.101:5555      通过下面的命令可以切 ...

  4. Vue.js使用-组件(上篇)

    1.什么是组件 组件可以理解为定义的一个view模块,可重复使用. 2.组件使用 1)创建组件 var myComponent = Vue.extend({ template: ' this is a ...

  5. 每日英语:China Bond Trading Dives

    SHANGHAI—Trading volume in China's bond market has plummeted in recent months, in another reminder o ...

  6. spark读取hdfs数据本地性异常【转】

    在分布式计算中,为了提高计算速度,数据本地性是其中重要的一环. 不过有时候它同样也会带来一些问题. 一.问题描述 在分布式计算中,大多数情况下要做到移动计算而非移动数据,所以数据本地性尤其重要,因此我 ...

  7. openfire ping的smack解决方案(维持在线状态)

    连接中关联如下: // iq提供者 roviderManager.getInstance().addIQProvider("ping", "urn:xmpp:ping&q ...

  8. Beginning SDL 2.0(4) YUV加载及渲染

    本文主要内容是基于的“Beginning SDL 2.0(3) SDL介绍及BMP渲染”(以下简称BS3)基础上,将BMP加载及渲染修改为YUV420或I420的原始视频格式.阅读完本部分内容相信你可 ...

  9. Eclipse - Mac Os Default JRE missing

    转:http://stackoverflow.com/questions/1736993/eclipse-mac-os-default-jre-missing 1) Follow Joshua's a ...

  10. Nginx作为负载均衡器upstream

    Nginx中与proxy模块结合使用的模块中,最常用的当属upstream模块.upstream模块可定义一个新的上下文,它包含了一组upstream服务器,这些服务器可能被赋予了不同的权重.不同的类 ...