Jmeter性能调优建议

前言

JMeter作为免费的开源压测软件有着非常强大的功能。支持四层到七层协议的测试,主要还是TCP协议,http,https,websocket(需要下载额外的拓展包来支持该协议测试)。JMeter的功能很丰富但本篇博客不对JMeter功能做过多详细介绍。相信很多同学在执行JMeter的过程中如对被测物实行高压力压测的过程中会报出类似于OOM(Out Of Memory)的错误。本文主要针对java.lang.OutOfMemoryError: Java heap space问题进行阐述,并对Jmeter执行压测时如何调优性能给出一些实际切实可行的意见。

内存耗尽问题描述

在进行JMeter压测时如果压测的线程组给的过大时,则整个测试过程就会报出形如“java.lang.OutOfMemoryError: Java heap space”的错误。首先要知道JMeter进行是一个java程序,所有的java程序都会在JVM虚拟机中执行。当JVM执行时会调用默认设定的设置和参数来运行程序。JMeter创建测试线程组时首先会找JVM申请内存,JVM找底层操作系统中获取内存,获取了内存的JMeter才可以为创建必要的线程对象分配可执行的内存空间。

设置过大线程导致内存占满

让我们来举个栗子:假如我想对一个nginx后端服务器做静态页面的压力测试,这个时候我设定了线程组的线程为50000个。并且设置了Ramp-up Period的时间为1秒,并且将Loop Count设置为“Forver”,这就意味着Jmeter需要开启5W个线程并且持续性的给nginx后端服务器发请求。
在这里插入图片描述
HTTP请求器配置如下
在这里插入图片描述
在jmx的配置文件中修改如下

      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">true</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">50000</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>

此时我们使用非GUI模式来运行JMeter的压测,我的测试机是一台32核64G内存的虚拟机,作为5W并发的压力机而言肯定是绰绰有余的了。然后我们执行了JMeter的压测之后结果并非如此。这里看到无法使用到更多的内存来为测试线程使用了。
在这里插入图片描述

性能调优点

1.调整JMeter堆栈内存大小
无论是在windows还是在linux的环境下,JMeter的所申请的内存是必须要加大的。有两个JAVA的参数直接影响着JMeter能够使用的系统内存为多少,一个是“Xms”(代表初始化堆栈内存的大小),一个是“Xmx(代表最大内存池可以分配的大小)”。如果你的测试机器只跑JMeter一个JAVA应用程序,那么建议Xmx和Xms保持一致。Xmx和Xms保持一致是为了减少JVM内存伸缩,减少维护伸缩带来的成本。

注意:在默认情况下现在JMeter5版本开辟的内存空间为1G,这也是它的最大内存。在实际测试的过程中,默认JMeter内存配置情况下,开启3W个线程来处理http的静态访问求基本上就达到了极限。再往上加的可能会报OOM的错误。

2.64位操作系统内存配置大小
内存加大之后我们需要考虑一下所给的内存是否有效的转化给JMeter所使用。通常64位的JVM消耗的内存会比32位的JVM大1.5倍,主要原因对象指针在64位架构下,指针的长度会翻倍。在创建线程对象时会消耗比32位JVM更多的内存空间。为了解决这种内存消耗问题在64位JVM中默认开启了压缩普通对象指针(CompressdOops)。当对象被读取,解压,存入heap中时进行压缩。结合零基压缩优化(Zero Based Compressd Oops:通过改变正常指针的随机地址分配特性强制从零开始做分配,进一步提高了压缩效率,但是当内存分配超过了32G的情况则JVM采取随机地址分配)。所以为什么说JMeter内存分配尽量在32位的系统上避免分配4G以上空间,在64位的操作系统尽量避免分配32G以上的空间。

如果你觉得你的内存足够大,想引入大内存分配来解决JVM过度的内存开销问题,从而实现高内存(例如:128G)的性能高于32G内存的性能。你可以尝试开启更大的内存对齐空间(当然官方已经不建议你这样做了),JAVA8中给出的参数如下:

-XX:ObjectAlignmentInBytes=alignment
Sets the memory alignment of Java objects (in bytes). By default, the value is set to 8 bytes. The specified value should be a power of two, and must be within the range of 8 and 256 (inclusive). This option makes it possible to use compressed pointers with large Java heap sizes.
The heap size limit in bytes is calculated as:
4GB * ObjectAlignmentInBytes
Note: As the alignment value increases, the unused space between objects will also increase. As a result, you may not realize any benefits from using compressed pointers with large Java heap sizes.

注意:为什么说64位的操作系统尽量避免分配32G以上的内存呢。主要原因在于“-XX:ObjectAlignmentInBytes中ObjectAlignmentInBytes默认给的是8,所以4*8就是32G”。当你增加对齐值时,对象之间未使用的空间也会增加,所以增加JAVA堆大小可能并不会带来更多的好处。

  1. 垃圾回收机制
    在JAVA中有

  2. 使用非GUI模式
    可以的话,尽量使用JMeter的非GUI模式来进行压测。JMeter设计的初衷并不是在使用GUI模式下产生高负载,GUI在一定程度上冻结并消耗资源,这样会更容易产生一些不准确的性能测试结果。对于JMeter来说GUI存在的意义主要在于可视化输出结果,编写你的测试计划和debug你的测试计划。

  3. 升级JMeter与JAVA版本
    尽可能使用最新版本的JMeter来进行性能测试,新版本的JMeter会使用新版的JRE和JDK,这样会一定程度上带来性能的提升。

  4. 压测过程中禁用监听器
    实行压测的过程中尽可能少使用监听器,最好别用。启用监听器会导致额外的内存开销,这会消耗测试中为数不多的内存资源。比较明智的做法是使用非GUI模式,将测试结果全部保存到“.jtl”的文件中。测试完成之后将jtl格式的结果文件导入的JMeter中再进行结果分析。

当然你要说想在测试的过程中监控整个测试的情况可不可以,当然是可以的。这里博主查看了一些国外博主怎么解决这个问题。国外的博主通过启用Taurus的一个web框架来运行JMeter,这样结果可以实时的呈现在web页面中。有兴趣的可以看看Taurus框架的介绍。
在这里插入图片描述

  • 谨慎使用断言
    添加到测试计划的每个测试元素都将被处理。这样会占用较多的CPU和内存。这种资源的占用适用于所有的断言,尤其是比较断言。比较断言消耗大量资源和内存,所以在压力测试时慎重的使用断言和断言的数量。

  • 垃圾回收机制
    在JAVA中有大概五类的垃圾回收机制。分为Serial收集器,ParNew收集器,Parallel收集器,Cms收集器,G1收集器。在垃圾回收机制上应该尽量减少垃圾回收器带来的内存和CPU的性能损耗。当然这些并不会对开启线程数有着决定性的影响,属于细节性的微调。这里比较推荐使用G1垃圾回收器。G1垃圾回收器的特点如下:

  • 支持很大的堆,高吞吐量

  • 支持多CPU垃圾回收

  • 在主线程暂停时,使用并行回收

  • 在主线程运行时,使用并发回收。

实际优化结果

针对32核64G的测试机,编辑jmeter文件,设置HEAP与GC回收器的参数如下:

: "${HEAP:="-Xms32g -Xmx32g -XX:NewSize=20480m -XX:MaxNewSize=20480m -XX:MaxMetaspaceSize=2048m"}"
: "${GC_ALGO:="-XX:+UseG1GC -XX:+DisableExplicitGC  -XX:MaxGCPauseMillis=10 -XX:G1ReservePercent=20"}"

实际可承载的最大线程数可达到3.2W,但是这里我认为没有达到最好的结果,32G分配的内存并没有完全使用,仍有很多富裕的内存,但是再加大线程数仍然回报OOM的错误。
在这里插入图片描述

这里对比给出使用CMS收集器的配置和使用CMS运行3.2W线程的运行结果

: "${HEAP:="-Xms32g -Xmx32g -XX:NewSize=20480m -XX:MaxNewSize=20480m -XX:MaxMetaspaceSize=2048m"}"
: "${GC_ALGO:="-XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC  XX:CMSInitiatingOccupancyFraction=80 -XX:ReservedCodeCacheSize=2048m -XX:NewRatio 1"}"

实时打印结果,如下图。这里可以看出G1的收集器相对CMS收集器占用内存相对较高。
在这里插入图片描述

系统优化

附上我在虚拟机上做的系统优化。

# cat /etc/sysctl.conf 
# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
net.ipv4.tcp_syncookies = 0
fs.file-max = 12553500
fs.nr_open = 12453500
kernel.shmall= 1048576
kernel.shmmax = 1887436
kernel.msgmax = 65536
kernel.sysrq = 0
kernel.pid_max= 65536
net.core.netdev_max_backlog = 2000000
net.core.rmem_default = 699040
net.core.rmem_max = 50331648
net.core.wmem_default = 131072
net.core.wmem_max = 33554432
net.core.somaxconn = 65535
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_mem = 1048576 1572864 2097152
net.ipv4.tcp_rmem = 4096 4194304 8388608
net.ipv4.tcp_wmem = 4096 4194304 8388608
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_window_scaling = 1
vm.swappiness = 0

#TCP connection recovery
net.ipv4.tcp_max_tw_buckets = 6000000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 10
net.ipv4.route.max_size = 5242880
net.ipv4.ip_forward = 1
net.ipv4.tcp_timestamps = 1
#开启对于TCP时间戳的支持,若该项设置为0,则下面一项设置不起作用

#TCP connection manager
net.ipv4.tcp_max_syn_backlog = 655360
net.ipv4.tcp_syn_retries = 6
net.ipv4.tcp_retries1 = 3
net.ipv4.tcp_retries2 = 6

#TCP keepalive
net.ipv4.ip_local_port_range = 1025 65534
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3

文件打开数优化
# cat /etc/security/limits.conf
root soft nofile 1024000
root hard nofile 1024000

  • 6
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于JMeter分布式压测的性能调优,以下是一些建议: 1. 确保服务器性能:确保所有参与分布式压测的服务器都具备足够的硬件资源,包括CPU、内存、磁盘和网络带宽等。这可以避免服务器成为性能瓶颈。 2. 合理设置线程数:在分布式压测中,线程数是一个重要的参数,需要根据服务器性能和测试需求进行合理的设置。过多的线程数可能会导致服务器负载过高,过少则无法发挥最大的压测能力。 3. 优化测试计划:确保测试计划中只包含必要的请求和步骤。删除不必要的请求和断言,减少额外的开销。 4. 合理使用断言:断言是用来验证响应结果的,但过多的断言会增加服务器的负载。只保留必要的断言,并使用高效的断言方式,如使用正则表达式替代XPath。 5. 配置合理的结果收集:JMeter会将每个请求的结果收集起来,这可能会产生大量的数据。合理配置结果收集,包括选择需要收集的数据、设置合理的采样率等。 6. 使用合适的远程启动配置:在分布式压测中,需要配置一个或多个远程服务器来执行测试。确保远程服务器的配置合理,包括JMeter版本一致、Java版本一致等。 7. 启用持久连接:启用HTTP请求中的Keep-Alive选项,可以减少连接的建立和断开次数,提高压测性能。 8. 使用合适的协议:对于不同的压测场景,选择合适的协议,如使用HTTP协议进行简单的Web应用压测,使用JDBC协议进行数据库压测等。 9. 监控和调优:在压测过程中,实时监控服务器的性能指标,如CPU使用率、内存使用率等。根据监控结果进行调优,如适时调整线程数、增加服务器资源等。 以上是一些常见的JMeter分布式压测性能调优建议,根据具体情况和需求,还可以进一步进行优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值