本文正在参与 “性能优化实战记录”话题征文活动
前言
说起性能测试就来劲了,因为它作为测试领域为数不多需要深厚技术功底的技能之一;
但是,入门容易,深耕难。想要在这一门技术里修炼成大咖,没有个5-6年技术积累是很难达到的;
关键在于性能问题的发生是无规律的,却又有一定的套路在里面,需要不断学习和总结经验。
这里我也建议:学习不一定要拿自己公司的项目,而是先去拿别人开源的项目来练手,自己设定性能问题,然后通过现象了解性能问题的本质,以达到性能优化的目的。
环境准备
这里不只是环境搭建,而且包括脚本开发、数据构造、监控平台等等一切为执行性能测试场景而准备
- 推荐前面搭建的Jforum系统,脚本也是前面开发完成好的,只需要按照场景执行观察并分析结果就行
浏览帖子的场景
- 运行场景:设置30个线程,没有思考时间,持续5分钟。。。
- 场景现象:一段时间后tps急剧下降、服务无响应、日志报错、无慢sql;服务器 cpu 100%、平均负载飙高回落、内存正常
- 分析日志:%TOMCAT_HOMT%/logs/catalina.out
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fd5f811d800 nid=0xc94 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fd5f811b000 nid=0xc93 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fd5f810c800 nid=0xc92 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fd5f80dc000 nid=0xc90 in Object.wait() [0x00007fd5e8bfa000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000f81c9a98> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fd5f811d800 nid=0xc94 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fd5f811b000 nid=0xc93 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fd5f810c800 nid=0xc92 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fd5f80dc000 nid=0xc90 in Object.wait() [0x00007fd5e8bfa000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000f81c9a98> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
复制代码
性能诊断
top命令监控cpu资源、抓出占用CPU过高的进程pid、jstack检索线程状态; 分析日志,出现了BLOCKED线程阻塞,造成的原因有:宽带、中间件线程池、数据库连接池等; 分析原因内网压测不大可能是带宽问题,可能是tomcat中间件线程池不够,线程池占满无法创建新连接,等待连接。
配置优化
先猜测是tomcat线程池不够,那就调整20为300
反复测试
性能测试是一个不断重复的过程,一直测试到发现不了问题为止。
- 用一个场景运行,发现一段时间后tps急剧下降,再去分析服务日志:
Exception in thread "http-bio-8080-exec-28" Exception in thread "http-bio-8080-exec-27" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:57)
at java.nio.ByteBuffer.allocate(ByteBuffer.java:335)
at sun.nio.cs.StreamDecoder.<init>(StreamDecoder.java:251)
at sun.nio.cs.StreamDecoder.<init>(StreamDecoder.java:231)
at sun.nio.cs.StreamDecoder.forInputStreamReader(StreamDecoder.java:69)
at java.io.InputStreamReader.<init>(InputStreamReader.java:100)
at freemarker.cache.FileTemplateLoader$4.run(FileTemplateLoader.java:210)
at freemarker.cache.FileTemplateLoader$4.run(FileTemplateLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at net.jforum.util.legacy.clickstream.ClickstreamFilter.doFilter(ClickstreamFilter.java:59)
java.lang.OutOfMemoryError: GC overhead limit exceeded
复制代码
看到OutOfMemoryError错误就豁然开朗了,先肯定前面的猜测对了,优化了服务配置
性能分析
OOM内存出现了问题,也就是GC堆内存开销超出限制,可能是程序在不断的GC但是缓慢,来不及给下一个线程使用;所以还是有可能是程序的内存分配问题,即使增加配置:-XX:-UseGCOverheadLimit,还是会抛出异常:
java.lang.OutOfMemoryError: Java heap space
,这样就可以解释为堆内存溢出了。
堆内存原因:需要调整tomcat的jvm参数增大堆内存Xms\Xmx
- jvm参数优化
增加tomcat线程池、调整tomcat的jvm参数128m至1g
再次运行场景
- 结果,不再出现异常情况,支持30个线程不断并发,tps上升后到达一个稳定过程,RT也没有增高,维持在3s以内。