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

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内存泄漏案例

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

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

相关文章

doc无法编译java文件_java编译成jar文件.doc

java编译成jar文件Java程序打包成jar包(2012-06-08 10:28:23)转载▼标签:分类: 方法一:通过jar命令jar命令的用法:下面是jar命令的帮助说明:用法:jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-p…

python 档案管理系统_Python 写入档案的 4 个方法

在 Python 写入档案内容跟读取档案差不多, 也很简单方便,以下会介绍用 Python 逐行读取档案内容的 4 种方法。在看例子前先要了解开启档案的参数, 一般上读取档案会用 “r”, 即唯读的意思, 如果要写入档案, 分别可以用 “w” (即 write 的意思) 或 “a” (即 appen…

高可用性(HA),会话复制,多VM Payara群集

抽象 在研究如何创建高可用性(HA)时,我发现了会话复制的多机Payara / GlassFish群集,无法在一个参考中找到所需的一切。 我认为这将是一个普遍的需求并且很容易找到。 不幸的是,我的假设是错误的。 因此,本…

java时间日期工具类_java日期处理工具类

java日期处理工具类import java.text.DecimalFormat;import java.text.ParsePosition;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;import java.util.GregorianCalendar;import java.util.Locale;import java.util.Random;import java…

python运行结果闪退_Pyhton TestCase运行闪退与失败,原因不详。。。

把源码贴上来,希望某位大神可以指点迷津:"""Unit test for odbchelper.pyThis program is part of "Dive Into Python", a free Python book forexperienced programmers. Visit http://diveintopython.org/ for thelatest ver…

java.jsp.jdbc_Java-jsp使用JDBC访问数据库时显示乱码是怎么回事?

1.JSP页面编码你改成utf-8;2.servlet容器的编码格式你改成utf-8;以tomcat为例:找到你的安装目录tomcat下面 > conf > server.xml用记事本打开添加如下的代码:URIEncoding"utf-8"protocol"HTTP/1.1"port"8080"connec…

python实例讲解wxpythonhyh123_Python实例讲解 -- wxpython 基本的控件 (按钮)

使用按钮工作在wxPython 中有很多不同类型的按钮。这一节,我们将讨论文本按钮、位图按钮、开关按钮(toggle buttons )和通用(generic )按钮。如何生成一个按钮?在第一部分(part 1)中,我们已经说明了几个按钮的例子,所以这里我们只…

检测Java Web应用程序而无需修改其源代码

与其他系统进行交互时,大多数Java Web应用程序都使用标准Java接口。 使用接口javax.servlet.Servlet来实现基于HTTP的服务,例如网页或REST服务器。 使用JDBC接口java.sql.Statement和java.sql.Connection实现数据库交互。 这些标准几乎是通用的&#xff…

如何快速弄懂一个新模型_如何评估创业项目是否靠谱?一个新的模型 | 创创锦囊...

要判断一个创业项目是否靠谱,是否能拥有广阔的市场和巨大的增长潜力,不仅是投资人关心的话题,更是每一个创业者在创业过程中不断思考的问题。投资人关注大趋势、大机会,遵循自上而下的思维模型,在心仪的赛道上寻找合适…

java编译找不到符号 int age=in.nexint()_Java报错找不到符号,小白自学求大佬解决...

import java.util.*;public class guess_1{public static void main(String[] args){Scanner innew Scanner(System.in);System.out.println("--------猜拳游戏--------");System.out.println("请出拳(1.剪刀 2.石头 3.布)");int personin.nextInt();int c…

Java命令行界面(第24部分):MarkUtils-CLI

本系列中有关使用Java解析命令行参数的第一篇文章介绍了Apache Commons CLI库。 这是本系列中介绍的基于Java的命令行解析库中最古老的,而且可能是最常用的之一。 Apache Commons CLI确实显示了它的时代,特别是与一些更现代的基于Java的命令行处理库相比…

view如何接受json_如何将你的 ThinkJS 项目部署到 ZEIT 上

编者按:本文作者奇舞团前端开发工程师李喆明。什么是 ZEITZEIT(https://zeit.co) 是免费的云平台,支持部署静态网站以及 Serverless 函数。Serverless 是近几年比较火的概念,简单去理解就是你只需要去实现具体的业务逻辑,而与最终…

python 小爱音箱集成_python控制小爱音箱自定义设备开关_修仙教程_小爱同学

send send_to_login(ipport,cookie,start_time,end_time)def play(): name info,播放 message_json {"action":"play","media":"app_ios"} path mediaplayer method player_play_operation send.sned_to_cmd(message_json,path,me…

Spring MVC和REST中@RestController和@Controller注释之间的区别

Spring MVC中的RestController注释不过是Controller和ResponseBody注释的组合。 它已添加到Spring 4.0中,以简化在Spring框架中RESTful Web Services的开发。 如果您熟悉REST Web服务,您就会知道Web应用程序与REST API之间的根本区别在于,Web…

java patriciatrie_明明白白以太坊Merkle Patricia Trie

在以太坊数据结构中,Merkle Patricia Trie始终是个绕不过去的坎,世界状态,交易,交易收据等都是以这种树的形式存储在区块链数据库中,并将树root hash保存在区块头里。可以说不弄懂这种树的原理就没有办法真正明白以太坊…

python打开串口失败_python 如何防止串口通信失败?

python 对串口的操作我用的是“线程轮寻”方式。就是打开串口后,启动一个线程来监听串口数据的进入,有数据时,就做数据的处理(也可以发送一个事件,并携带接收到的数据)。我没有用到串口处理太深的东西。客户的原程序不能给你&…

java 调用scala 类_如何使用java类加载器调用带参数的scala函数?

我正在寻找一些将scala jar加载到java类加载器的指导。当我使用java jar文件时,下面的函数对我有效。其中,arr是一个java.net.URL数组,用于我需要加载到类加载器中的所有jar。val classLoader new URLClassLoader(arr, this.getClass().getClassLoader())val clazz classLoad…

python c4.5完整代码_python实现c4.5/Id3自我练习

import numpy as npclass DecisionTree:"""决策树使用方法:- 生成实例: clf DecisionTrees(). 参数mode可选,ID3或C4.5,默认C4.5- 训练,调用fit方法: clf.fit(X,y). X,y均为np.ndarray类型…

jdeveloper_适用于JDeveloper 11gR2的Glassfish插件

jdeveloper众所周知, ADF Essentials是使用Java构建Web应用程序的绝佳框架,它可以自由开发和部署。 您在Glassfish(3.1)服务器上部署ADF Essentials应用程序。 但是,JDeveloper并不带有嵌入式Glassfish服务器&#xff…

php接收get数组数据,来自HTTP的PHP注入GET数据用作PHP数组键值

我想知道在以下场景中是否存在可能的代码注入(或任何其他安全风险,如读取您不应该使用的内存块等等),其中来自HTTP GET的未经过处理的数据用于代码中PHP作为数组的键.这应该将字母转换为字母顺序. a到1,b到2,c到3 …. HTTP GET“字母”变量应该有值字母,但是你可以理解任何东西…