设为首页 - 加入收藏 - 网站地图 SecYe安全 Www.SecYe.Com - 国内网络信息安全IT技术门户网
当前位置:SecYe > 数据库 > 数据库理论 > 正文

Apache Jmeter3.0压测数据库OOM的Bug排查

时间:2016-10-19 16:34 来源:JMeter 作者:SecYe安全 阅读:

Apache Jmeter 2.13(以下简称Jmeter2)版本后,2.X系列就作古了。前些日子,Apache Jmeter 3.0(以下简称Jmeter3)版本正式发布,新生的事物,功能肯定强大了很多,但作为开源产品,稳定性自然要打些折扣,一位同学前几天在使用Jmeter3时不幸中招。

二、问题描述

原本好用的JDBC请求脚本,压测数据库,使用Jmeter3版本时直接OOM,回退至Jmeter2,一切正常,啥也别说,肯定是又出Bug了,本着求实的精神,咱来看看Jmeter3为毛OOM了。

三、排查过程

1.找出OOM的对象

首先在Jmeter3中配置输出HeapDump文件,在jmeter.sh中增加以下一行:

JVM_ARGS="-Xms512m -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=~/gc.hprof

 

然后启动Jmeter程序,观察Jmeter的JVM内存变化,再次OOM了,把输出的gc.hprof文件,利用Memory Analyzer来分析,得到一堆index文件如下图:

利用Eclipse的MAT插件,加载全部的index文件,查看哪个对象占用了内存,如下图:

看到AbstractJDBCTestElement类中的perConnCache对象,占用了441M的内存,确定是这个对象内存泄露了。

2.找出导致OOM的原因

先来看看这个perConnCache究竟是做什么用的

/*** Cache of PreparedStatements stored in a per-connection basis. Each entry of this* cache is another Map mapping the statement string to the actual PreparedStatement.* At one time a Connection is only held by one thread*/private static final Map> perConnCache =new ConcurrentHashMap>();

这是一个HashMap,用来缓存每个连接(即Connection,下文简称为Conn)的PreparedStatements。而笔者的压测脚本中,最大Conn数设定的是10个,就是说,perConnCache的size最大应该是10才对,OK,估计问题就出在这里,笔者猜测应该是每次请求的Conn都具有不同的HashCode。

为了验证这个猜测,笔者用远程Debug来查看一下,每次获取Conn时的HashCode。

Debug后发现,在Jmeter3中有一个有趣的现象,每次获取到的Conn果然具有不同的HashCode,但是Jmeter3与DB的Conn总数却刚好是10个,就是说——物理连接的总数量并没有变,但每次从连接池获取到的Connection对象却一直在变。

笔者又Debug了一下Jmeter2,Jmeter2的结果是符合预期的,物理连接的总数是10个,perConnCache的size也是10,每次从连接池中获取的Conn也都能在perConnCache中命中。

3.锁定问题点

不论是Jmeter2还是Jmeter3,JDBCSampler的实现都是一样的:

如图所示,这里有3个关键操作,获取Conn,执行SQL,关闭Conn。在执行SQL的步骤中,perConnCache会进行缓存验证,如果没有命中,则把新的Conn缓存。

每次getConnection()都是连接池的操作,所以,问题就应该出在Jmeter3使用的连接池实现上。

4.OOM原因解析

下面来解释一下,为什么Jmeter3的连接池就会出现OOM的问题。

因为Jmeter3使用的是dbcp2,在getConnection()方法中,每次不是直接返回Connection对象,而是都会新new一个PoolGuardConnectionWrapper对象,看下面的代码:

可以看到,Conn并没有变,但是每次取到的对象都是重新new的包裹类PoolGuardConnectionWrapper,每次都是新的对象,HashCode当然就不一样了,在HashMap无法命中,所以最终每一次getgetConnection()时,perConnCache都产生一个新成员,最终OOM。

为啥Jmeter2没有这个问题呢?Jmeter2使用的是excalibur-datasource,每次Connection取到的就是com.mysql.jdbc.JDBC4Connection对象本身,自然在perConnCache里面就可以命中了

四、修改方式:

方式一:抛弃dbcp2,回退到excalibur-datasource,问题解决

方式二:修改perConnCache缓存机制,不是用Connection作为Key,而是利用sql的内容作为key,当然也可以直接删除这个Cache。

笔者尝试去掉了缓存机制,没有再发生OOM问题,压测结果对整体的TPS影响也不大

五、结论:

Jmeter3稳定性还有待考验,近期不推荐使用,建议使用Jmeter2.13来替代。

本文来源:SecYe安全网[http://www.secye.com] (责任编辑:SecYe安全)

点击复制链接 与好友分享!

顶一下
(0)
0%
踩一下
(0)
0%