`

【优化】多线程访问数据库导致内存泄露的优化过程

 
阅读更多
本文转自:http://www.blogjava.net/persister/archive/2010/01/14/309530.html

1、这家公司有一个数据库代理程序,用于数据库服务器的代理,游戏服务器执行sql指令,DBAgent接受此指令,执行一些组织后,调用JDBC执行数据库操作,然后将结果返回。

2、发生的问题:内存一直升高,处理客户端请求的线程并不多(高峰期大概300左右吧),数据库上的连接数也不多(100的样子)。运行5-6天,基本上内存就用完了,而且得不到数据库的连接。他们非常急,我就试着接下这个项目。

3、接到这个优化项目,查看了他们的部分代码。发现连接池写得有些问题,得不到数据库连接后wait,但是不会接到任何有效的notify,也就是说只要一等待就会超时。还有其他的问题,一开始我以为是这个问题,修改后让他们去跑,结果还是一样,内存上去后一直下不来,最后崩溃。

4、通过这个发现就是内存泄露了。一开始用jprofiler测试,发现内存上去后就Out of memory了,而且hashmap和long[]的对象非常多一直下不去。但是找不到这些对象是怎么产生的。折磨了我好几天,还请教了很多人,都得不到答案。后来发现是java启动参数中内存参数设置的太低了,本来需要100多M的内存,你就设置给16M,不崩溃才怪呢。于是改成了128M。结果内存上去之后,到达一个峰值就下来了,然后震荡,但一直就没有上去。那程序没有泄露?可是生产上怎么泄露了呢?

5、就在我基本上要放弃的时候,我想到了测试环境可能真实环境不同,有必要看一下他们生产服务器上虚拟机的运行情况。记得Java有自带的工具查询java虚拟机运行情况的。jmap这个工具可以查看jvm中运行实例的个数以及实例的类名。让他们的人用了下这个工具,将结果发给我了,我一看,吓了一大跳。排在第一位的是int[],竟然达到了1G。最有问题的是com.mysql.jdbc.PreparedStatement对象,达到了6万多。com.mysql.jdbc.ResultSetImpl和java.util.HashMap$Entry[]也达到了6万多。不用说,肯定是PreparedStatement没有关闭。

6、查看源代码,发现PreparedStatement对象都在finally块中关闭了,怎么会泄露呢?找了1个小时没有找到,就去洗澡了。在洗澡的时候突然想到,里面有一个for循环,PreparedStatement对象可能被赋值N次,那前面的N-1次不就没有关闭啊,对,找到答案了。赶紧擦干身子出来找到那段代码:

1  String[] valuesArray = value.split(";");
 2  for (int i = 0 ;i < valuesArray.length;i++){
 3 
 4 String[] valueArray = valuesArray[i].split(",");
10                     ps = conn.prepareStatement(sqlbean.getSql());
11                     for(int k = 0;k <valueArray.length;k++){
12                         if("s".equalsIgnoreCase(paraTypeArray[k])){
13                             ps.setString(k+1,valueArray[k]);
14                         }else if("i".equalsIgnoreCase(paraTypeArray[k])){
15                             ps.setInt(k+1,Integer.parseInt(valueArray[k]));
16                         }
17                     }
18 
19                     rsString = "" + ps.executeUpdate();
20 
21 }


确实如此,循环的N-1个PreparedStatement对象没有关闭,导致了泄漏。解决办法就是将
ps = conn.prepareStatement(sqlbean.getSql());

移到for循环的外面,这样就没有问题了。不过从这段代码也可以看出,写得也是在是烂,这个干吗放到
循环的里面呢,本身从语言上来说就有问题。管它呢,解决问题就行了,呵呵。
几乎兴奋了一个晚上。第二天找他们的人一说,他们说是循环N次的,不只是一个值。问题不就解决了?Great。

7、让他们去测试运行吧。运行第一天的晚上九点(这是高峰期)以后,内存非常平稳。问题彻底解决了。

总结这次的优化项目:
对Java虚拟机的认识提高了。对java性能测试工具(JProfiler)更加熟练了,可以和eclipse集成呢,非常方便。
分享到:
评论

相关推荐

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    多线程put的时候可能导致元素丢失 68 解决方案 68 【集合】ConcurrentHashMap的get(),put(),又是如何实现的?ConcurrentHashMap有哪些问题?ConcurrentHashMap的锁是读锁还是写锁? 69 【集合】HashMap与HashTable...

    【最新版】wechat_devtools_1.02.2004020.dmg【亲测可用】最好的微信开发者工具

    A 新增 多线程 worker 支持单步调试 A 新增 公众号网页调试中新增 url 收藏夹 A 新增 wx.compressImage 调试 A 新增 小游戏关系链互动数据开发支持 A 新增 支持小游戏 JSServer A 新增 小游戏节点审查插件 A ...

    超级有影响力霸气的Java面试题大全文档

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    精易模块[源码] V5.15

    8、修正“编码_Utf8到Unicode”频繁操作导致内存泄漏的BUG,感谢易友【◆野蛮vE儿】反馈。 9、修正“目录_是否存在”,当存在无反缀文件时返回真的BUG,感谢易友【@飞灵】反馈。 10、新增“系统_信息框Ex”定时信息...

    java 面试题 总结

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    WINDOWS 内部原理 (八)

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    Windows内部原理(十一):存储和文件系统

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    WINDOWS 内部原理(十)驱动和硬件的管理

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    X-Scan v3.1

    采用多线程方式对指定IP地址段(或单机)进行安全漏洞检测,支持插件功能,提供了图形界面和命令行两种操作方式,扫描内容包括:远程服务类型、操作系统类型及版本,各种弱口令漏洞、后门、应用服务漏洞、网络设备...

    windows 内部原理(一)

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    WINDOWS 内部原理(九)

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    x-SCAN -V3.3-CN.

    采用多线程方式对指定IP地址段(或单机)进行安全漏洞检测,支持插件功能。扫描内容包括:远程服务类型、操作系统类型及版本,各种弱口令漏洞、后门、应用服务漏洞、网络设备漏洞、拒绝服务漏洞等二十几个大类。对于...

    WINDOWS 内部原理 (三)

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    WINDOWS 内部原理(四)

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    WINDOWS 内部原理(五)

    在全面了解了Windows的体系结构和程序运行方式后,我们将进一步介绍访问越界、缓冲溢出、内存泄露等故障的原理,并理论联系实际,带领大家使用调试工具来解决一些常见的问题。 深入研究Windows内部原理系列之十六...

    X-Scan

    功能简介: &lt;br&gt; 采用多线程方式对指定IP地址段(或单机)进行安全漏洞检测,支持插件功能,提供了图形界面和命令行两种操作方式,扫描内容包括:远程服务类型、操作系统类型及版本,各种弱口令漏洞、后门、应用...

    强大的扫描工具x-scan

    采用多线程方式对指定IP地址段(或单机)进行安全漏洞检测,支持插件功能。扫描内容包括:远程服 务类型、操作系统类型及版本,各种弱口令漏洞、后门、应用服务漏洞、网络设备漏洞、拒绝服务漏洞等 二十几个大类。...

    易语言程序免安装版下载

    修改外部数据库在4.12版中导致的不兼容问题,并增加了对MS SQL Server数据库中image和text字段类型的说明。 7. 修改扩展界面支持库一,禁止透明标签在父窗口刷新时自动刷新,以解决其导致窗口刷新缓冲的问题。 8....

    loadrunner测试资料

    测试可以帮助找到一些大型的问题,如死机、崩损、内存泄漏等,因为有些存在内存泄漏问题的程序,在运行一两次时可能不会出现问题,但是如果运行了成千上万次,内存泄漏得越来越多,就会导致系统崩滑。  Loadrunner...

    Java经典入门教程pdf完整版

    简单地说,Java具有如下特点:简单的、面向对象、平台无关、多线程、分布式、安全、 晑性能、可靠的、解释型、自动垃圾回收等特点。 这里只解释一下平台无关和分布式,其余的在后面会逐步接触到 3:平台无关 所谓平台...

Global site tag (gtag.js) - Google Analytics