热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何发现内存泄露

如果您是一名新手或中级Java开发人员,还不知道如何使用Java虚拟机(JVM)生产环境,那么在Java应用程序中查找内存泄漏可能是大海捞针。但是,根据您的评测工具,您可以轻松地分



如果您是一名新手或中级Java开发人员,还不知道如何使用Java虚拟机(JVM)生产环境,那么在Java应用程序中查找内存泄漏可能是大海捞针。但是,根据您的评测工具,您可以轻松地分析Java内存消耗,同时获得对Java生产应用程序中堆的即时洞察。但是,在详细介绍如何在java web应用程序中发现内存泄漏之前,让我们先了解一下什么是java内存泄漏,这种泄漏的可能原因以及处理此问题的修复过程。


Java内存泄漏


内存泄漏只是由PermGen中保留的引用链接引起的,不能被垃圾回收。


听起来像胡言乱语,对吧?好吧,保持冷静,我继续解释。因为web容器使用类到类装入器映射系统来隔离web应用程序,而且因为类是由其名称和加载它的类装入器唯一标识的。因此,可以有一个同名的类,在一个JVM中加载多次,每个类都有一个不同的类装入器。


java.lang.Class

从长远来看,这可能会成为一个非常大的参考图。别忘了这些类直接加载到PermGen中。因此,从web应用程序中保留对特定对象的引用会将web应用程序加载到PermGen中的每个类固定下来。即使web应用程序被重新加载后,这些引用通常仍会保留,每次重新加载时,更多的类会被固定或卡在PermGen中,而PermGen在适当的时候会被填满。


什么是PermGen?


PermGen是永久生成(PermGen)的缩写,是JVM中的一个堆,专门用于存储JVM的Java类的内部表示形式,以及被扣留的字符串实例。简单地说,它是一个独立于主Java堆的独占堆位置,JVM在那里注册与已加载类相关的元数据。


不过,大多数java servlet容器和WebSocket技术都支持 org.apache.catalina.core.JreMemoryLeakPreventionListener 通过扩展(如ApacheTomcat7.0及更高版本)初始化。尽管如此,如果出现更复杂的情况,比如重新加载时的PermGen错误和应用程序本身引起的bug干扰,那么包含这个内存泄漏处理程序是不够的。当tomcat servlet没有导致泄漏时,这会变得更有趣,应用程序也不是(至少不是直接的),而是JRE代码中由第三方库触发的bug。


随着Java开发工具包JDK6(更新7或更新版本)的出现,JDK附带了一个方便的工具,它使我们的生活变得更加轻松。这个工具称为 VisualVM ;它是一个图形化工具,可以无缝地连接到任何JVM,除了允许您导航该堆之外,还允许您从JVM堆中排出不需要的垃圾。在tomcat servlet中,web应用程序的类加载器也是一个名为 org.apache.catalina.loader.WebappClassLoader . 因此,如果我们的Tomcat实例只部署了一个web应用程序,那么堆中应该只有一个此类的实例。但如果有更多的话,我们就有漏洞了。


如何在java web应用程序中查找内存泄漏的步骤


现在我们已经解决了这个问题,让我们快速深入了解如何检测和避免Java内存泄漏的步骤。让我们立即深入研究如何使用 VisualVM 来解决这个问题。


步骤:


1. 打开命令提示符终端,输入下面的命令启动visualvm;


${javahome}/bin/jvisualvm

将弹出一个类似于图1的窗口。


如何在java web应用程序中发现内存泄漏


图1:JavaVisualVM



右键单击左侧边栏中的Tomcat,然后选择“Heap Dump”。


如何在java web应用程序中发现内存泄漏



图2:堆转储:单击“OQL控制台”按钮。


点击控制台顶部的“转储”按钮。这将打开一个控制台,允许您查询堆转储。对于本练习,我们希望找到 org.apache.catalina.loader.WebappClassLoader.So 在结果控制台中输入以下命令;


select x from org.apache.catalina.loader.WebappClassLoader x


图3:“OQL查询编辑器控制台”:输入上面的命令并单击execute


在本例中,VisualVM发现了两个web应用程序类装入器实例;一个用于web应用程序本身,另一个用于Tomcat管理器应用程序。


使用Tomcat管理器应用程序在重新启动web应用程序 http://localhost:8080/manager/html 并获取另一个Tomcat进程的堆转储。



图4:重新启动web应用程序并导航回“OQL控制台”以再次重复步骤3


注意上面步骤中包含了一个额外的实例。这是因为这3个实例中的一个应该由垃圾回收器收集,但事实并非如此。多亏了Tomcat,我们可以很容易地分辨出哪个实例没有被垃圾回收,因为所有活动类装入器都设置了字段名:“started”设置为“true”。


为了找到无效的实例,请单击每个类装入器实例,直到找到“started”字段设置为“false”的实例。



图5:单击堆转储中的每个类装入器实例,找出有问题的实例-“started”字段设置为false


现在我们已经找到了导致泄漏的类装入器,我们需要确定哪个对象持有对类装入器的引用。显然,大量的对象将被许多其他对象引用,但从本质上讲,只有有限数量的这些对象会构成引用图的根。这些是我们特别感兴趣的对象。


因此,在instances选项卡的底部窗格中,右键单击构成引用图根的对象实例,然后选择“Show Nearent GC root”。


结果窗口应该是这样的;



图6:右键单击构成引用图根的对象实例,然后选择“Show Nearent GC root”。


右键单击实例并选择“显示实例”。



图7:右键单击实例并选择“Show Instance”。


由此,我们可以推断这是 sun.awt.AppContext 类型。我们还可以看到AppContext中的contextClassLoader字段包含对WebappClassLoader的引用。因此,这是导致内存泄漏的错误引用。现在我们来看看是什么实例化了 sun.awt.AppContext 类型,对于初学者。


首先,我们用以下代码在调试模式下重新启动Tomcat;


export JPDA_SUSPEND=y
${TOMCAT_HOME}/bin/catalina.sh jpda

然后,我们继续远程调试类加载序列——在这种情况下,我将使用Eclipse来完成这项工作。另外,我们需要在 sun.awt.AppContext 使用Open Type命令(Shift+Control+T)导航到 sun.awt.AppContext 类型。


右键单击大纲窗格中的类名并选择 Toggle Class Load Breakpoint “切换类加载断点”。


此后,我们需要通过将调试器连接到Tomcat实例并让调试器在 sun.awt.AppContext 装载;


将调试器连接到Tomcat实例,并将调试器设置为在 sun.awt.AppContext 已加载。


就这样!它已经被JavaBeans框架实例化,在这个实例中,Oracle Universal Connection Pool (UCP)正在使用它。因此,我们可以注意到contextClassLoader是一个最终的字段,它看起来像是一个单独的字段;所以我们可以假设这个字段只在AppContext的实例化过程中设置一次。


所以我们可以推断这个字段只在实例化期间设置了一次。


我很方便地将上面的代码添加到我的servlet上下文侦听器中,使它在应用程序启动期间执行,并达到了修复这个特定内存泄漏的预期效果。


如何在Java Web应用程序中发现内存泄漏-小结


总而言之,JEE应用程序的“PermGen”内存不足错误通常驻留在应用程序本身(或应用程序使用的库)中,并且通常由JRE库中的类组成,这些类包含对web应用程序类加载器的引用或web应用程序类加载器实例化的对象的引用。


查找泄漏原因的整个过程是利用Java应用程序性能监视( APM )解决方案。就像Fusion Reactor的堆分析器为未收集的web应用程序类装入器实例提供源代码,然后为直接或间接保留类装入器的根GC对象提供源。当您找到这个对象时,您现在可以使用APM的内置调试器来发现这个对象是如何被实例化的,然后设计一种方法来修改它的常规行为。这样做是为了使它不会永远保持类装入器引用。




推荐阅读
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 提升 Kubernetes 集群管理效率的七大专业工具
    Kubernetes 在云原生环境中的应用日益广泛,然而集群管理的复杂性也随之增加。为了提高管理效率,本文推荐了七款专业工具,这些工具不仅能够简化日常操作,还能提升系统的稳定性和安全性。从自动化部署到监控和故障排查,这些工具覆盖了集群管理的各个方面,帮助管理员更好地应对挑战。 ... [详细]
  • 深入理解 Java 控制结构的全面指南 ... [详细]
  • Spring框架的核心组件与架构解析 ... [详细]
  • 本文深入探讨了 hCalendar 微格式在事件与时间、地点相关活动标记中的应用。作为微格式系列文章的第四篇,前文已分别介绍了 rel 属性用于定义链接关系、XFN 微格式增强链接的人际关系描述以及 hCard 微格式对个人和组织信息的描述。本次将重点解析 hCalendar 如何通过结构化数据标记,提高事件信息的可读性和互操作性。 ... [详细]
  • 本指南介绍了如何在ASP.NET Web应用程序中利用C#和JavaScript实现基于指纹识别的登录系统。通过集成指纹识别技术,用户无需输入传统的登录ID即可完成身份验证,从而提升用户体验和安全性。我们将详细探讨如何配置和部署这一功能,确保系统的稳定性和可靠性。 ... [详细]
  • 深入解析CAS机制:全面替代传统锁的底层原理与应用
    本文深入探讨了CAS(Compare-and-Swap)机制,分析了其作为传统锁的替代方案在并发控制中的优势与原理。CAS通过原子操作确保数据的一致性,避免了传统锁带来的性能瓶颈和死锁问题。文章详细解析了CAS的工作机制,并结合实际应用场景,展示了其在高并发环境下的高效性和可靠性。 ... [详细]
  • 本文深入解析了 jQuery 中用于扩展功能的三个关键方法:`$.extend()`、`$.fn` 和 `$.fn.extend()`。其中,`$.extend()` 用于扩展 jQuery 对象本身,而 `$.fn.extend()` 则用于扩展 jQuery 的原型对象,使自定义方法能够作为 jQuery 实例的方法使用。通过这些方法,开发者可以轻松地创建和集成自定义插件,增强 jQuery 的功能。文章详细介绍了每个方法的用法、参数及实际应用场景,帮助读者更好地理解和运用这些强大的工具。 ... [详细]
  • 在日常的项目开发中,测试环境和生产环境通常采用HTTP协议访问服务。然而,从浏览器的角度来看,这种访问方式会被标记为不安全。为了提升安全性,当前大多数生产环境已经转向了HTTPS协议。本文将详细介绍如何在Spring Boot应用中配置SSL证书,以实现HTTPS安全访问。通过这一过程,不仅可以增强数据传输的安全性,还能提高用户对系统的信任度。 ... [详细]
  • Java测试服务器调试指南详细介绍了如何进行远程调试,并深入解析了Java Xdebug参数的使用方法。本文首先概述了Java内置的调试功能,重点介绍了JDB这一类似于GDB的强大调试工具。通过实例演示,读者可以掌握在测试环境中高效调试Java应用程序的技巧,包括配置远程调试环境和优化调试参数,以提高开发效率和代码质量。 ... [详细]
  • Hadoop 2.6 主要由 HDFS 和 YARN 两大部分组成,其中 YARN 包含了运行在 ResourceManager 的 JVM 中的组件以及在 NodeManager 中运行的部分。本文深入探讨了 Hadoop 2.6 日志文件的解析方法,并详细介绍了 MapReduce 日志管理的最佳实践,旨在帮助用户更好地理解和优化日志处理流程,提高系统运维效率。 ... [详细]
  • 在项目开发过程中,掌握一些关键的Linux命令至关重要。例如,使用 `Ctrl+C` 可以立即终止当前正在执行的命令;通过 `ps -ef | grep ias` 可以查看特定服务的进程信息,包括进程ID(PID)和JVM参数(如内存分配和远程连接端口);而 `netstat -apn | more` 则用于显示网络连接状态,帮助开发者监控和调试网络服务。这些命令不仅提高了开发效率,还能有效解决运行时的各种问题。 ... [详细]
  • 深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用
    深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用 ... [详细]
  • 分布式开源任务调度框架 TBSchedule 深度解析与应用实践
    本文深入解析了分布式开源任务调度框架 TBSchedule 的核心原理与应用场景,并通过实际案例详细介绍了其部署与使用方法。首先,从源码下载开始,详细阐述了 TBSchedule 的安装步骤和配置要点。接着,探讨了该框架在大规模分布式环境中的性能优化策略,以及如何通过灵活的任务调度机制提升系统效率。最后,结合具体实例,展示了 TBSchedule 在实际项目中的应用效果,为开发者提供了宝贵的实践经验。 ... [详细]
author-avatar
mobiledu2502885385
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有