java内存泄漏案例

一周前,我被要求修复一个有内存泄漏问题的webapp。 考虑到过去两年左右的时间里我已经看到并修复了数百个泄漏,我想这有多难。

但是事实证明这是一个挑战。 12小时后,我发现该应用程序中不少于5个漏洞,并设法修复了其中4个漏洞。 我认为这将是值得分享的经历。 对于那些不耐烦的人–总而言之,我发现了

  • MySQL驱动程序启动后台线程
  • 重新部署时未卸载java.sql.DriverManager
  • BoneCP从错误的类加载器加载资源
  • 数据源已注册到JNDI树中,阻止了卸载
  • 使用终结器的连接池与在单独线程中运行的Google的参考队列实现相关联

当前的应用程序是一个简单的Java Web应用程序,具有一些连接到关系数据库的数据源,中间是Spring以将内容粘合在一起,并将简单的JSP页面呈现给最终用户。 没有魔术。 还是我想。 男孩,我错了。

第一站 -MySQL驱动程序。 显然,最常见MySQL驱动程序会在后台启动线程,以清理未使用和未关闭的连接。 到目前为止,一切都很好。 但是要注意的是,这个新创建的线程的上下文类加载器是您的Web应用程序类加载器。 这意味着在运行此线程并且您尝试取消部署Webapp时,它的类加载器被甩了下来-加载了所有类。

显然,从2012年7月到2013年2月,该错误已被发现。 您可以按照MySQL问题跟踪器中的讨论进行操作。 最终实现的解决方案是API的shutdown()方法,开发人员在重新部署之前应该知道要调用该方法。 好吧,我没有。 我敢打赌,你们当中有99%的人也没有。

在典型的Java Web应用程序中,有一个适合此类关闭挂钩的好地方,即ServletContextListener类contextDestroyed()方法。 每次销毁servlet上下文时,都会调用此特定方法,例如,这种情况通常发生在重新部署期间。 可能有相当多的开发人员意识到这个地方的存在,但是实际上有多少人意识到需要清理这个特定的钩子呢?

回到该应用程序,该应用程序还没有被修复。 我的第二个发现还与上下文类加载器和数据源有关。 使用com.jdbc.myslq.Driver时,它将自身注册为java.sql.DriverManager类中的驱动程序。 同样,这是有良好意图的。 毕竟,这是您的应用程序用来确定在连接到数据库URL时如何为每个查询选择正确的驱动程序的方法。 但是您可能会猜到一个陷阱:该DriverManager是在引导类加载器中加载的,而不是在Web应用程序的类加载器中加载的,因此在重新部署应用程序时无法将其卸载。

现在使事情真正变得奇怪的是,没有一般的方法可以自行注销驱动程序。 对您尝试注销的类的引用似乎是故意向您隐藏的。 在这种特殊情况下,我很幸运,应用程序中使用的连接池能够注销驱动程序。 万一我记得问。 回顾过去的类似案例,这是我第一次看到在连接池中实现这种功能。 在此之前,我曾经不得不枚举在DriverManager中注册的所有JDBC驱动程序,以找出应该注销的驱动程序。 我无法向任何人推荐这种体验。

我想应该是这样。 同一应用程序中的两次泄漏已经可以忍受一个以上。 错误。 泄漏报告中盯着我的第三个问题是sun.awt.AppContext及其静态字段mainAppContext。 什么? 我不知道该类应该做什么,但是我很确定手头的应用程序没有以任何方式使用AWT 。 因此,我启动了一个调试器,以找出是谁加载了此类(以及为什么)。 另一个惊喜:它是com.sun.jmx.trace.Trace.out()。 您能想到com.sun.jmx类将之称为sun.awt类的充分理由吗? 我当然不能。 但是,该类堆栈源自连接池BoneCP 。 跳过导致该特定内存泄漏的代码行的绝对零方式。 解? 我的ServletContextListener.contextInitialized()中的以下魔咒:

Thread.currentThread().setContextClassLoader(null); // Force the AppContext singleton to be created and initialized without holding reference to WebAppClassLoder sun.awt.AppContext.getAppContext();

但是我仍然没有做完:有些东西还在泄漏。 在这种情况下,我发现我们的应用程序将此数据源绑定到InitialContext() JNDI树,这是一种很好的,标准化的方法,用于绑定对象以供将来发现。 但是,再次强调–使用这种好东西时,您必须通过在非常相同的contextDestroy()方法中从JNDI树中解除绑定此数据源来清理自己。

好吧,到目前为止,我们遇到了相当合乎逻辑的问题,尽管很少见并且有些晦涩难懂的问题,但是有了一些推理和google-fu很快就解决了。 我的第五个也是最后一个问题就是这样。 我仍然因为OutOfMemoryError:PermGen而使应用程序崩溃。 Plumbr和Eclipse MAT都向我报告说,罪魁祸首是把我的类加载器扣为人质的一个线程,名为com.google.common.base.internal.Finalizer。 “这家伙到底是谁?” –在黑暗吞没我之前,我最后的想法是。 几个小时和四杯咖啡后,我发现自己盯着三行:

emf.close();
emf = null;
ds = null;

很难准确地回忆一下在此期间发生的事情。 我对WeakReferences , ReferenceQueues , Finalizers , Reflection有遥远的记忆,而我第一次在野外看到PhantomReference 。 即使到了今天,我仍然无法完全解释为什么连接池使用终结器以及将终结器绑定到在单独线程中运行的Google的参考队列实现的原因以及目的。

我也无法解释为什么关闭javax.persistence.EntityManagerFactory (在上面的代码中命名为emf并保存在应用程序自己的类之一中的静态引用中)的原因; 因此,我不得不手动使该引用无效。 以及对该工厂使用的数据源的类似静态引用。 我确信Java的GC可以整天处理循环引用,但是,即使对于他来说,类,静态引用,对象,终结器和引用队列的魔力环似乎也太难了。 因此,这是我漫长的职业生涯中的第一次,我不得不取消Java参考。

我是一个谦虚的人,因此不能说我在短短12个小时内能最有效地找到以上所有方法。 但是我必须承认,过去三年来我几乎一直在处理内存泄漏。 而且我什至拥有自己的创作Plumbr来帮助我(实际上,五分之四的泄漏是Plumbr在30分钟左右的时间内发现的)。 但是要真正解决这些泄漏,我花了一个多日的时间。

总体而言-在Java EE和/或类加载器世界中,显然有些问题。 开发人员必须记住所有这些挂钩和配置技巧,这是不正常的,因为这根本不可能。 毕竟,我们喜欢用头脑去做一些富有成效的事情。 而且,从与两个流行的servlet容器( Tomcat和Jetty )捆绑在一起的变通办法可以看出,问题很严重。 但是,解决该问题不仅需要缓解某些症状,还需要解决潜在的设计错误。

参考: 寻找 内存泄漏:我们的JCG合作伙伴 Nikita Salnikov Tarnovski在Plumbr Blog博客上的案例研究 。

翻译自: https://www.javacodegeeks.com/2013/03/hunting-down-memory-leaks-a-case-study.html

java内存泄漏案例

java内存泄漏案例_寻找内存泄漏:一个案例研究相关推荐

  1. 内存泄漏分析_调查内存泄漏第2部分–分析问题

    内存泄漏分析 这个小型系列的第一个博客介绍了如何创建一个非常泄漏的示例应用程序,以便我们可以研究解决服务器应用程序上基于堆的问题的技术. 它展示了Producer-Consumer模式的一个大问题,即 ...

  2. java直接内存为什么快_直接内存与 JVM 源码分析

    直接内存(堆外内存) 直接内存有一种叫法,堆外内存. 直接内存(堆外内存)指的是 Java 应用程序通过直接方式从操作系统中申请的内存.这个差别与之前的堆.栈.方法区,那些内存都是经过了虚拟化.所以严 ...

  3. 服务器内存超限问题_服务器内存爆满最佳处置方案

    内存爆满截图: 分析:内存持续飙升,应该是有大量内存一直没有释放,考虑僵尸对象,僵尸进程,最简单的就是重启服务器,但是就无法找到罪魁祸首了. 验证:top命令查看活跃进程的资源使用情况.(top命令是 ...

  4. 服务器内存 知乎_服务器内存和普通内存有什么区别?可以通用吗?

    平时大家接触最多的应该是普通内存,也就是我们所说的PC内存,一般来说,内存越大,可用的缓存就越大,电脑的运行速度就越快.用在服务器上是同理,服务器的内存越大,可用缓存就越大,网站的速度也越快. 服务器 ...

  5. 苹果内存不够怎么办_手机内存清理了还是不够用?不知道这些方法,真是太可惜了...

    手机会越用越卡?手机内存不够大,内存不够怎么办?最好的方法就是清理自己的内存,那么你知道自己手机里面的内存怎样清理干净吗?是否还会有一些清理不掉的缓存呢,就算你点了清理,甚至删除了一俩个软件,手机仍然 ...

  6. 大数据项目开发案例_大数据分析技术——项目案例1(猫眼电影数据分析上)...

    壹 猫眼Top100电影数据分析概述 从这一节开始,我们就综合利用已学到的一些分析技术来尝试做一些比较复杂的实际数据分析项目.在这些实际的项目案例中,我们将会看到一个完整的数据分析流程:数据清理--数 ...

  7. oracle电梯案例,Oracle技术嘉年华的一个案例,redo的那些事,连载一

    电梯 Oracle技术嘉年华的一个案例,redo的那些事,连载三 在刚刚结束的Oracle技术嘉年华大会上,eygle大师的演讲中提到了有关于数据安全的一个案例,大致的意思是数据库管理员修改了自己账户 ...

  8. 内存泄漏代码_调查内存泄漏第1部分–编写泄漏代码

    内存泄漏代码 前几天,我发现了这个小问题:该服务器运行了一段时间,然后掉下来了. 然后通过启动脚本重新启动,整个过程重复进行. 听起来并没有什么坏处,因为它虽然对数据造成了重大损失,但对业务的重要性并 ...

  9. java hashmap is遍历_关于内存:在Java(或Scala)中遍历HashMap的HashMap

    我创建了一个类Foo,该类具有返回Array的方法toArray(). 现在,我有一个将字符串映射到HashMaps的HashMap,后者将对象映射到Foo.那是: HashMap> 我想创建一 ...

最新文章

  1. C语言自学《八》---- C语言知识总结
  2. 轻松搞定面试中的红黑树问题
  3. 洛谷 [SDOI2009]晨跑
  4. Android如何在测试程序中删除被测应用私有的原始数据
  5. 苏炳添:发C刊与拿冠军相比,哪个更难?
  6. 气泡为何是球形?这是一个数学问题!
  7. 怎么查看表用了那个序列_知识分享008:怎样在手机上用萤石云查看海康威视监控录像...
  8. 从 JavaScript 属性描述器剖析 Vue.js 响应式视图
  9. 王者为什么有些服务器在维护,王者荣耀服务器正在维护中怎么回事 进不去怎么办...
  10. RocketMQ 拉取消息-通信模块
  11. redis发布与订阅的实现
  12. 破圈法求最小生成树_【学科加油站】悉大COMP9007 最小生成树知识点总结!
  13. hplus java_Java HijrahDate plus(long, TemporalUnit)用法及代码示例
  14. 通联数据举办公益讲座 探讨金融大数据发展机会
  15. ECCV2020超分辨率方向论文整理笔记
  16. C++开发斗地主(QT)第三篇之动画发牌与位置计算
  17. 端午节,我用Python画了一盘粽子
  18. winpe加载raid_为WinPE添加RAID卡驱动的几种步骤
  19. C++-Cmake指令:cmake_minimum_required
  20. 异步FIFO的verilog代码实现(包含将满和将空逻辑)

热门文章

  1. mybatis example处理and、or关系的方法
  2. JavaFX UI控件教程(十三)之Table View
  3. 漫画:什么是分布式事务
  4. 接口 Closeable
  5. win7禁用其他软件只启用自定义软件的方法
  6. SpringCloud(笔记)
  7. java synchronized 使用_Java中synchronized的使用实例
  8. 拉取git的分支项目
  9. lin通讯从节点同步间隔场_汽车行业必须知识--CAN FD通讯
  10. python3.0什么时候发布的_Django 3.0 发布说明