Jmeter性能测试-GC相关

https://www.cnblogs.com/danqiu/p/6009016.html

 

Jmeter性能测试-GC相关

1.GC相关

HotSpot虚拟机将其物理上划分为两个–新生代(young generation)和老年代(old generation)。
新生代(Young generation): 绝大多数最新被建立的对象会被分配到这里,因为大部分对象在建立后会很快变得不可到达,因此不少对象被建立在新生代,而后消失。对象从这个区域消失的过程咱们称之为”minor GC“。html

老年代(Old generation): 对象没有变得不可达,而且重新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正因为其相对较大的空间,发生在老年代上的GC要比新生代少得多。对象从老年代中消失的过程,咱们称之为”major GC“(或者”full GC“)java

图中的持久代( permanent generation )也被称为方法区method area)。他用来保存类常量以及字符串常量。所以,这个区域不是用来永久的存储那些从老年代存活下来的对象。这个区域也可能发生GC。而且发生在这个区域上的GC事件也会被算为major GC。redis

新生代是用来保存那些第一次被建立的对象,他能够被分为三个空间数据库

  •  一个伊甸园空间(Eden 
  •  两个幸存者空间(Survivor )
  • 绝大多数刚刚被建立的对象会存放在伊甸园空间。
  • 在伊甸园空间执行了第一次GC以后,存活的对象被移动到其中一个幸存者空间。
  •   此后,在伊甸园空间执行GC以后,存活的对象会被堆积在同一个幸存者空间。
  •  当一个幸存者空间饱和,还在存活的对象会被移动到另外一个幸存者空间。以后会清空已经饱和的那个幸存者空间。
  • 在以上的步骤中重复几回依然存活的对象,就会被移动到老年代。

jstat 是HotSpot JVM提供的一个监控工具api

jstat –gc  $<pid$> 1000缓存

 

S0C       S1C       S0U    S1U      EC         EU          OC         OU         PC         PU         YGC     YGCT    FGC      FGCT     GCT服务器

3008.0   3072.0    0.0     1511.1   343360.0   46383.0     699072.0   283690.2   75392.0    41064.3    2540    18.454    4      1.133    19.588网络

3008.0   3072.0    0.0     1511.1   343360.0   47530.9     699072.0   283690.2   75392.0    41064.3    2540    18.454    4      1.133    19.588并发

3008.0   3072.0    0.0     1511.1   343360.0   47793.0     699072.0   283690.2   75392.0    41064.3    2540    18.454    4      1.133    19.588eclipse

 

这些信息很重要,由于它们展现了GC处理到底花费了多少时间。

在这个例子中,YGC 是217而YGCT 是0.928,这样在简单的计算数据平均数后,你能够知道每次新生代的GC大概须要4ms(0.004秒),而full GC的平均时间为33ms。

可是,只看数据平均数常常没法分析出真正的GC问题。这是主要是由于GC操做时间严重的误差(换句话说,假如两次full GC的时间是 67ms,那么其中的一次full GC可能执行了10ms而另外一个可能执行了57ms。)为了更好地检测每次GC处理时间,最好使用 –verbosegc来替代数据平均数。

为何须要优化GC

或者说的更确切一些,对于基于Java的服务,是否有必要优化GC应该说,对于全部的基于Java的服务,并不老是须要进行GC优化,但前提是所运行的基于Java的系统,包含了以下参数或行为:

  • 已经经过 -Xms 和–Xmx 设置了内存大小
  • 包含了 -server 参数
  • 系统中没有超时日志等错误日志

换句话说,若是你没有设定内存的大小,而且系统充斥着大量的超时日志时,你就须要在你的系统中进行GC优化了。

可是,你须要时刻铭记一条GC优化永远是最后一项任务。

我为GC优化概括了两个目的:

  1. 一个是将转移到老年代的对象数量降到最少
  2. 另外一个是减小Full GC的执行时间

 

将转移到老年代的对象数量降到最少

按代的GC机制由Oracle JVM提供,不包括能够在JDK7以及更高版本中使用的G1 GC。换句话说,对象被建立在伊甸园空间,然后转化到幸存者空间,最终剩余的对象被送到老年代。某些比较大的对象会在被建立在伊甸园空间后,直接转移到老 年代空间。老年代空间上的GC处理会新生代花费更多的时间。所以,减小被移到老年代对象的数据能够显著地减小Full GC的频率。减小被移到老年代空间的对象的数量,可能被误解为将对象留在新生代。可是,这是不可能的。取而代之,你能够调整新生代空间的大小。

减小Full GC执行时间

Full GC的执行时间比Minor GC要长不少。所以,若是Full GC花费了太多的时间(超过1秒),一些链接的部分可能会发生超时错误。

  • 若是你试图经过消减老年代空间来减小Full GC的执行时间,可能会致使OutOfMemoryError 或者 Full GC执行的次数会增长。
  • 与之相反,若是你试图经过增长老年代空间来减小Full GC执行次数,执行时间会增长。

所以,你须要将老年代空间设定为一个“合适”的值。

影响GC性能的参数

正如咱们在第二篇文章结尾提到的,不要幻想“某我的设定了GC参数后性能获得极大的提升,咱们为何不和他用同样的参数?”,由于不一样的Web服务所建立对象的大小和他们的生命周期都不尽相同。

简单来讲,若是一个任务的执行条件是A,B,C,D和E,一样的任务执行条件换为A和B,你会以为哪一个更快?从通常人的直觉来看,在A和B条件下执行的任务会更快。

Java GC参数也是相同的道理,设定一些参数不但没有提升GC执行速度,反而可能致使他更慢。GC优化的最基本原则是将不一样的GC参数用于2台或者多台服务器,并进行对比,并将那些被证实提升了性能或者减小了GC执行时间的参数应用于服务器。请谨记这一点。

下面这个表格列出了GC参数中与内存大小相关的,能够影响性能的参数。

1GC优化须要考虑的Java参数

定义

参数

描述

堆内存空间

-Xms

Heap area size when starting JVM

启动JVM时的堆内存空间。

 

-Xmx

Maximum heap area size

堆内存最大限制

新生代空间

-XX:NewRatio

Ratio of New area and Old area

新生代和老年代的占比

 

-XX:NewSize

New area size

新生代空间

 

-XX:SurvivorRatio

Ratio ofEdenarea and Survivor area

伊甸园空间和幸存者空间的占比

我在进行GC优化时常用-Xms,-Xmx和-XX:NewRatio。-Xms和-Xmx是必须的。你如何设定NewRatio 会对GC性能产生十分显著的影响。有些人可能会问如何设定Perm区域的大小?你能够经过-XX:PermSize 和-XX:MaxPermSize参数来设定,

当OutOfMemoryError 错误发生而且是因为Perm空间不足致使时,另外一个可能影响GC性能的参数是GC类型。下表列出了全部可选的GC类型(基于JDK6.0)

在分析监控结果后,决定是否进行GC优化

在检查GC状态的过程当中,你应该分析监控结果以便决定是否进行GC优化,若是分析结果代表执行GC的时间只有0.1-0.3秒,那你就不必浪费时间去进行GC优化。可是,若是GC的执行时间是1-3秒,或者超过10秒,GC将势在必行。

可是,若是你已经为Java分配了10GB的内存,而且不能再减小内存大小,你将没法再对GC进行优化。在进行GC优化 以前,你必须想清楚你为何要分配如此大的内存空间。假如当你分1 GB 或 2 GB内存时出现OutOfMemoryError ,你应该执行堆内存转储(heap dump),并消除隐患。

注意:

堆内存转储是一个用来检查Java内存中的对象和数据的文件。该文件能够经过执行JDK中的jmap命令来建立。在建立文件的过程当中,Java程序会暂停,所以不要再系统执行过程当中建立该文件。

若是GC执行时间知足下面全部的条件,就意味着无需进行GC优化了。

  • Minor GC执行的很快(小于50ms)
  • Minor GC执行的并不频繁(大概10秒一次)
  • Full GC执行的很快(小于1s)
  • Full GC执行的并不频繁(10分钟一次)

上面提到的数字并非绝对的;他们根据服务状态的不一样而有所区别,某些服务可能知足于Full GC每次0.9秒的速度,但另外一些可能不是。所以,针对不一样的服务设定不一样的值以决定是否进行GC优化。

  • 设定内存空间大小

下表展现了内存空间大小,GC执行次数以及GC执行时间三者间的关系。

    • 大内存空间
      • 减少GC执行次数
      • 增长GC执行时间
    • 小内存空间
      • 减少GC执行时间
      • 增长GC执行次数

关于如何设置内存空间的大小,没有惟一的标准答案。若是服务 器资源足够,并且Full GC也可能在1秒内完成,设置为10GB固然可行。。但绝大多数服务器并非这样,当内存设为10GB时,可能要花费10~30秒来执行Full GC。固然,执行时间会随对象的大小而改变。

鉴于如此,咱们应该如何设定内存空间大小呢?一 般来讲,我建议为500MB。不过请注意这不是让你将WAS的内存参数设置为–Xms500m 和–Xmx500m。根据优化GC以前的状态,若是 Full GC执行以后内存空间剩余300MB,那么最好将内存设置为1GB(300MB(默认程序占用)+ 500MB(老年代最小空间)+200MB(空闲内存))。也就是说你要为老年代额外设置500MB。所以,若是你有三个执行服务器,内存分别设置为 1GB,1.5GB,2GB,而且检查结果。

理论上来说,GC执行速度应该遵循1GB> 1.5GB> 2GB,所以1GB执行GC速度最快。可是并不说明1GB空间的Full GC会花费1秒而2GB空间会花费2秒。时间取决于服务器的性能和对象的大小。所以,最佳的方式是创建尽量多的衡量指标来监控他们。

对于内存空间大小,你应该额外设定NewRatio参数。 NewRatio参数是新生代和老年代空间的比例,即XX:NewRatio=1意味着新生代与老年代之比为1:1。对于1GB来讲就是新生代和老年代各 500MB。若是NewRatio为2,意味着新生代老年代之比为1:2,所以该值越大,老年代空间越大,新生代空间越小。

这看似一件不是很重要的事情,但NewRatio参数会显著地影响整个GC的性能。若是新生代空间很小,会用更多的对象被转移到老年代空间,这样致使频繁的Full GC,增长暂停时间。

你能够简单的认为NewRatio 为1是最佳的选择,可是,有时可能设置为2或3更好,我就见过不少这样的例子。

如何最快的完成GC优化?对比性能测试的结果 应该是最快地方法,为每一台服务器设置不一样的参数并监控他们的状态,强烈建议至少监控1或2天的数据。可是,当你对GC优化是,你要确保每次执行相同的负 载。而且请求的比率,例如URL都应该是一致的。不过,即使对于专业测试人员要想精确的控制负载也是很难的,并要花费大量的时间准备。所以,相对来讲比较 方便和容易的方法是调整才参数,以后花费较长的时间收集结果。 

示例1

下面这个例子针对 Service S的优化,对于最近被部署的 Service S,Full GC花费了太长的时间。

请看 jstat –gcutil的执行结果。

 

1

2

S0 S1 E O P YGC YGCT FGC FGCT GCT

12.16 0.00 5.18 63.78 20.32 54 2.047 5 6.946 8.993

 

最左边的Perm 空间对于最初的GC优化不是很重要,这一次YGC参数的值更加有用。

Minor GC和Full GC的平均值以下表所示

3Service SMinor GC Full GC的平均执行时间

GC 类型

GC 执行次数

GC 执行时间

平均

Minor GC

54

2.047

37 ms

Full GC

5

6.946

1,389 s

最重要的是下面两个数据

  • 新生代实际使用空间: 212,992 KB
  • 老年代实际使用空间: 1,884,160 KB

所以,总的内存空间为2GB,不算Perm空间的话,新生代与老年代之比为1:9。经过jstat和-verbosegc 日志进行数据收集,并把三台服务器按照以下方式设置。

  • NewRatio=2
  • NewRatio=3
  • NewRatio=4

一天以后,检查系统的GC日志后发现,在设置了NewRatio参数后很幸运的没有发生Full GC,

为何?

  • NewRatio=2: 45 ms
  • NewRatio=3: 34 ms
  • NewRatio=4: 30 ms

咱们看到NewRatio=4 是最佳的参数,虽然它的新生代空间最小,但GC时间确最短。设定这个参数以后,系统没有执行过Full GC。

为了说明这个问题,下面是服务之星一段时间后执行jstat –gcutil的结果

 

1

2

S0 S1 E O P YGC YGCT FGC FGCT GCT

8.61 0.00 30.67 24.62 22.38 2424 30.219 0 0.000 30.219

 

你可能会认为由于服务器接受的请求少才致使的GC执行频率降低。实际上,虽然Full GC没有执行,可是Minor GC被执行了 2,424次。

示例2

这是一个针对ServiceA的例子,咱们经过公司内部的应用性能管理系统(APM)发现JVM暂停了至关长的时间(超过8秒),所以咱们进行了GC优化。咱们找到了Full GC执行时间过长的缘由,并着手解决。

进行GC优化的第一步,就是咱们添加了-verbosegc参数,并获得以下结果。

1:进行GC优化以前的STW时间

如上图所示,由HPJMeter自动生成的图片之一。X坐标表示JVM执行的时间。Y坐标表示每次GC的时间。CMS绿点,表示Full GC结果。Parallel Scavenge蓝点,表示Minor GC结果。

以前我曾经说过CMS GC是最快的,可是上面的的结果显示出于某种缘由,它最多花费了15秒。是什么致使这个结果?是否想起我以前提过的,CMS在进行内存清理时,会变慢。与此同时,服务的内存被设定为 –Xms1g和–Xmx4g ,且实际分配了4GB内存。

所以,我将GC类型从CMS改成Parallel GC。而且将内存改成2GB,设定NewRatio 为3。几小时以后我使用 jstat –gcutil获得以下结果

 

1

2

S0 S1 E O P YGC YGCT FGC FGCT GCT

0.00 30.48 3.31 26.54 37.01 226 11.131 4 11.758 22.890

 

相对于4GB时的15秒,Full GC变成了平均每次3秒。可是3秒同样比较慢,所以我设计了以下6种场景。

  • Case 1: -XX:+UseParallelGC -Xms1536m -Xmx1536m -XX:NewRatio=2
  • Case 2: -XX:+UseParallelGC -Xms1536m -Xmx1536m -XX:NewRatio=3
  • Case 3: -XX:+UseParallelGC -Xms1g -Xmx1g -XX:NewRatio=3
  • Case 4: -XX:+UseParallelOldGC -Xms1536m -Xmx1536m -XX:NewRatio=2
  • Case 5: -XX:+UseParallelOldGC -Xms1536m -Xmx1536m -XX:NewRatio=3
  • Case 6: -XX:+UseParallelOldGC -Xms1g -Xmx1g -XX:NewRatio=3

那一个最快呢?结果显示,内存越小,结果越好。下图展现了Case6的结果。这是GC的性能最好。最长的响应时间只有1.7秒。平均时间在1秒以内。

2Case6的时间图表

基于以上结果。咱们按照Case6调整了GC参数。可是,这致使了天天晚上都会发生OutOfMemoryError。在这里很难解释具体的缘由。简单来讲,批处理程序致使了内存泄漏。相关的问题已经被解决。

若是对GC日志只分析很短的时间就贸然对全部服务器进行优化是很是危险的。请时刻牢记,你必须同时分析GC日志和应用程序。

咱们回顾了两个关于GC优化的例子,正如我以前提到的,例子中提到的GC参数,能够设置在相同的服务器之上,但前提是他们具备相同的CPU,操做系统,JDK版本以及运行着相同的服务。可是不要直接把我用过的参数用到你的服务至上,它们未必能很好的工做。

 

切实地调优

若是测试的结果知足了预期,那么你不须要对程序进行性能调优。若是没有达到预期结果,你就应该执行调优来解决问题。接下来会经过实例讲解方法。

stop-the-world耗时过长

stop-the-world耗时过长多是因为GC参数不合理或者代码实现不正确。你能够经过分析工具或堆内存转储文件(Heap dump)来定位问题,好比检查堆内存中对象的类型和数量。若是在其中找到了不少没必要要的对象,那么最好去改进代码。若是没有发现建立对象的过程当中有特别 的问题,那么最好单纯地修改GC参数。

为了适当地调整GC参数,你须要获取一段足够长时间的GC日志,还必须知道哪些状况会致使长时间的stop-the-world。想了解更多关于如何选择合适的GC参数,能够阅读我同事的一篇博文:How to Monitor Java Garbage Collection

CPU使用率太低

当系统发生阻塞,吞吐量和CPU使用率都会下降。这多是因为网络系统或者并发的问题。为了解决这个问题,你能够分析线程转储信息(Thread dump)或者使用分析工具。阅读这篇文章能够得到更多关于线程转储分析的知识:How to Analyze Java Thread Dumps

你可使用商业的分析工具对线程锁进行精确的分析,不过大部分时候,只需使用JVisualVM中的CPU分析器,就能得到足够的信息。

CPU使用率太高

若是吞吐量很低可是CPU使用率却很高,极可能是低效率代码致使的。这种状况下,你应该使用分析工具定位代码中性能的瓶颈。可以使用的工具备:JVisualVMEclipse TPTP或者JProbe

调优方法

建议你使用以下方法对程序进行调优。

首先,检查性能调优是否必要。测量性能不是一件简单的工做,你也不能保证每次都得到满意的结果。所以若是程序已经知足预期性能需求,没必要在调优上增长额外的投入了。

问题只出在一个地方,你要作的就是去解决掉它。二八定律(Pareto principle)对性能调优一样适用。这不是说某个模块的低性能必定只源于一个问题,而是强调咱们应该在调优时把注意力放在影响最大的那个问题上。在处理好了最重要的以后,你才应该去解决剩下其余的。也就是建议一次只对一个问题进行修复。

另外须要考虑到气球效应(Balloon effect),有得必有失。你能够经过使用缓存来提升响应能力,可是当缓存逐渐增大,执行一次Full GC的时间也会更长。通常而言,若是你但愿内存使用率比较低,那么吞吐量和响应能力可能都会恶化。所以,要知道什么对本身程序来讲最重要的,而哪些又是次要的。

到此为止,你应该已经了解了如何对Java程序进行性能调优。为了介绍性能测定的具体过程,我不得不省略其中一些细节,不过我认为这些也足够应对大多数Java Web服务端程序了

 

2.操做相关

1.如何考虑一个性能需求

1. 一般拿到一个性能需求,须要了解接口模型,整个接口的使用比例和容量,系统结构和技术方案,数据库缓存和索引分布,设计出合理的测试计划,设计的原则是尽量真实的模拟线上状况。

2.根据设计出的测试计划,使用一个线程运行测试计划,得出测试结果,对比测试指标

3.以线性增加的方式增长并发数,比较测试结果是否为线性增加

4.测试结果平缓区一般表明着系统容量的最大区域,分析此时的测试结果,如 TPS和响应时间等

5.分析数据库TPS、QPS等数据,redis命中率,MQ吞吐率,javaGC,程序占用时间等各方面因素提升系统性能

6.经过jstack分析各模块的资源占用状况。

7.优化后的程序要通过大批量数据测试确保测试程序不会发生错误

8.根据测试概要报告,数据库监控数据等数据整合测试报告

9. 须要关注api层用的是长链接仍是短链接,长链接的话须要在jmeter里勾选keep alive

2.如何保证jmeter自身性能

1.使用命令行启动,减小界面形成的性能问题

2.命令行记录聚合报告,不要启动过多的其余监控报告

3.尽量关闭没必要要的日志,包括测试代码的log,jmeter自身日志。

4.确保测试脚本计时准确,需确认好那些步骤须要计时,那些在准备在计时以外

5.过多的并发使用分布式请求,当本地cpu,mem占用比较大时,修改jmeter.properties,增长远程remote-server,并启动(具体操做见jmeter操做手册)

3.如何分析测试结果

1.根据聚合报告分析90%,95%,和最大等指标,找出程序广泛的响应时间。

2.让开发人员查询最大响应时间时的日志,分析程序层面的执行任务状况

3.使用jstack等分析当前程序的资源等待状况,系统资源状况

值得关注的线程状态有:
     死锁,Deadlock(重点关注)
     执行中,Runnable  
     等待资源,Waiting on condition(重点关注)
     等待获取监视器,Waiting on monitor entry(重点关注)
     暂停,Suspended
     对象等待中,Object.wait() 或 TIMED_WAITING
     阻塞,Blocked(重点关注) 
     中止,Parked

示例图

4.分析gc结果,cpu,IO等状况,分析程序是否充分利用了系统资源

5.对比分析各并发数的程序响应状况。

分类: 性能测试

相关文章
相关标签/搜索