寻找内存泄漏:一个案例研究

一周前,我被要求修复一个有内存泄漏问题的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博客) 的案例研究 。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab中求积函数,MATLAB软件及高斯勒让德求积公式MATLAB软件及高斯勒让德求积公式.doc...

MATLAB软件及高斯勒让德求积公式MATLAB软件及高斯勒让德求积公式数 值 分 析 课 程 实 验 报 告2012—2013学年度 第二学期系别:数学与计算机科学学院实验课程数值分析班 级10级数学与应用数学2班学 号05姓 名杜宁峰指导教师陈耀庚实验题目学习编写高斯-勒让德计算程…

[HNOI2015] 落忆枫音

题目描述 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上。望着漫天飞舞的红枫,枫茜突然问出这样一个问题。 「相信吧。不然我们是什么,一团肉吗?要不是有灵魂......我们也不可能再见到你姐姐吧。」…

border,padding,margin盒模型理解

安静的敲着键盘&#xff0c;已势不可挡的姿势逼近php&#xff0c;我想我是一个幸福的人&#xff0c;未来不可期&#xff0c;做好现在&#xff0c;偶尔写着自己能看懂的API,慢慢悠悠的回味一下前端基础知识。 本文盒模型理解。 <!DOCTYPE html> <html lang"en&qu…

我的Java自定义线程池执行器

ThreadPoolExecutor是Java并发api添加的一项功能&#xff0c;可以有效地维护和重用线程&#xff0c;因此我们的程序不必担心创建和销毁线程&#xff0c;而将精力放在核心功能上。 我创建了一个自定义线程池执行程序&#xff0c;以更好地了解线程池执行程序的工作方式。 功能性…

php中mysqlstat函数,PHP函数mysql_stat介绍

&#xfeff;定义和用法mysql_stat() 函数返回 MySQL 服务器的当前系统状态。如果成功&#xff0c;则该函数返回状态。如果失败&#xff0c;则返回 false。语法mysql_stat(connection)提示和注释注释&#xff1a;mysql_stat() 目前只返回 uptime、threads、queries、open table…

全选、全部选、反选、提交

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title> </head> <body><form><input type"checkbox" name"items" value"足球"&…

Java中的模板方法设计模式

模板方法模式是一种行为设计模式&#xff0c;它为算法提供了基础方法&#xff0c;称为模板方法&#xff0c;该方法将其某些步骤推迟到子类中&#xff0c;因此算法结构相同&#xff0c;但某些步骤可以由子类根据上下文重新定义。 模板是指预设格式&#xff0c;例如HTML模板&…

android adb源码分析(1)

ADB是Android debug bridge的缩写&#xff0c;它使用PC机可以通过USB或网络与android设备通讯。 adb的源码位于system/core/adb目录下&#xff0c;先来看下编译脚本Android.mk&#xff1a; [plain] view plaincopy# Copyright 2005 The Android Open Source Project # # Andr…

matlab二次二阶距,用Matlab改进一次二阶矩法程序.doc

用Matlab编的计算结构可靠指标的改进一次二阶矩法程序(验算点法)题目&#xff1a;编制改进一次二阶矩法计算可靠指标的程序&#xff0c;并给出算例&#xff0c;要求提供源程序选取的算例为&#xff1a;zg(x,y)x*y-1140&#xff0c;其中x,y服从正态分布&#xff0c;μx38,Vx0.1,…

winform中的数据绑定

1. 简单的数据绑定 例1 using (SqlConnection conn new SqlConnection(ConfigurationManager.ConnectionStrings["connStr"].ToString())) { SqlDataAdapter sda new SqlDataAdapter("Select * From T_Class Where F_TypeProduct order by F_RootID,F_Order…

jQuery数据表和Java集成

jQuery DataTables是一个开放源代码插件&#xff0c;用于在浏览器中创建表。 它具有许多功能&#xff0c;例如排序&#xff0c;服务器端处理&#xff0c; JQUERY UI主题滚动。 该插件的下载链接&#xff1a; http://www.datatables.net/download/ 在本演示中&#xff0c;我…

CSS 属性 - 伪类和伪元素的区别

伪元素和伪类之所以这么容易混淆&#xff0c;是因为他们的效果类似而且写法相仿&#xff0c;但实际上 css3 为了区分两者&#xff0c;已经明确规定了伪类用一个冒号来表示&#xff0c;而伪元素则用两个冒号来表示。 :Pseudo-classes ::Pseudo-elements 但因为兼容性的问题&…

class-感知机Perception

1 感知机模型1.1 模型定义2 感知机学习策略2.1 数据的线性可分性2.2 学习策略3 学习算法3.1 算法原始形式3.2 收敛性3 学习算法的对偶形式1 感知机模型 感知机perceptron是二类分类问题的线性分类模型&#xff0c;输入为实例的特征向量&#xff0c;输出为实例的类别&#xff08…

图片资源 php,php URL图片资源传参生成对应尺寸图片

最近项目中需要上传大图&#xff0c;然后不同设备请求不同大小的图片&#xff0c;之前有用过一个通过URL参数来获取不同大小的图片的接口感觉设计方式请不错&#xff0c;于是就百度看看类似是如何实现的&#xff0c;找了几天找个两个功能类似的记录下。1、图片服务器 imagemagi…

Java中的方法调用有多昂贵

我们都去过那儿。 在查看设计不良的代码的同时&#xff0c;听听作者对人们永远不应该牺牲性能而不是设计的解释。 而且&#xff0c;您不能说服作者摆脱其500行方法&#xff0c;因为链接方法调用会破坏性能。 好吧&#xff0c;这可能在1996年左右是正确的。 但是自那时以来&…

UVa-116 Unidirectional TSP 单向旅行商

题目 https://vjudge.net/problem/uva-116 分析 设d[i][j]为从(i,j)到最后一列的最小开销&#xff0c;则d[i][j]a[i][j]max(d[i1][j1],d[i-1][j1]) 参考数字三角形,用逆推的方法,先确定最后一列d[i][n-1]a[i][n-1],再确定n-2列,此时d[i][n-2] a[i][n-2]min(d[i][n-1],d[i-1][n…

1.HTML

HTML简介 hyper text markup language 即超文本标记语言。 超文本: 就是指页面内可以包含图片、链接&#xff0c;甚至音乐、程序等非文字元素。 标准模板 <!DOCTYPE html> <html lang"en"><head> <meta charset"U…

error connection reset by peer 104

connection reset by peer的常见原因 1.服务器的并发连接数超过了其承载量&#xff0c;服务器会将其中一些连接关闭&#xff1b;2. errno 104错误表明你在对一个对端socket已经关闭的的连接调用write或send方法&#xff0c;在这种情况下&#xff0c;调用write或send方法后&…

php记住表单数据cookie,【PHP基础】cookies和session

1.Cookiescookie 常用于识别用户。cookie 是服务器留在用户计算机中的小文件。每当相同的计算机通过浏览器请求页面时&#xff0c;它同时会发送 cookie。通过 PHP&#xff0c;您能够创建并取回 cookie 的值。1.1、如何创建 cookie&#xff1f;setcookie() 函数用于设置 cookie。…

自己构建GlassFish 4.0快照

这篇文章是关于自己发布GlassFish 4.0快照的&#xff0c;其中包括一些黑客。 我找到了GlassFish FullBuild的官方说明&#xff0c;然后决定自己构建服务器。 有时&#xff0c;您可能不想等待团队升级GlassFish构建文件。 在本条目中&#xff0c;我将Artifactory称为私有Maven存…