`
xuexing
  • 浏览: 24030 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java内存泄漏分析(转)

阅读更多


Java语言相比C++的一个很大优势就是 Java可以自动管理内存的回收,这大大减少了程序员的负担。然而,Java并不是杜绝了所有的内存问题,还是会有内存泄漏的问题,只不过原因和C++是不一样的,所以出现得比较少。Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。详细说明可以看ibm developerworks上的文章(http://www-900.ibm.com/developerworks/cn/java/j-leaks/index_eng.shtml)。在现在代码检查工具越来越先进的情况下,C++的内存漏洞检查已经变得容易很多,但Java的内存漏洞由于机制不一样,反而无法通过工具直接检查出来,只要靠辅助工具检测,发现疑点,然后再手动解决。

下面是我调试一个大型软件系统内存泄漏问题的过程,把其中失败的做法也写出来了,因为都是有参考价值的:

现象:程序启动后一直运行正常,但启动某个模块后,每分钟多消耗1M的内存,持续增长到出现java.lang.OutOfMemoryError为止,而CPU占用率也是逐步上升至100%。

判断:由于CPU和内存都出现问题,无法判断哪个是主因,也可能是多个原因造成的,估计最可能是多线程或者内存泄漏的问题。

辅助工具:由于不是我自己写的程序,所以没法直接估计是代码的哪一部分的问题,所以需要使用辅助工具,我比较常用的是Eclipse的插件 ru.nlmk.eclipse.plugins.profiler(免费)和Borland公司的Borland Optimizeit Suite(收费)。因为我是用Eclipse来开发程序,所以使用前者比较方便,个人感觉使用时占用的系统资源少一点,而且在CPU检查方面是略强一点,但是后者在内存检查方面强大很多。

分析过程:当时由于CPU和内存都出现问题,无法判断哪个是主因,需要检测两方面的数据。内存泄漏需要等程序运行一段时间后才能看出来,所以检查很消耗时间。为了减少干扰,我把系统中所有不是必要的各线程逐个关闭来试验,然后把确认对内存问题没有影响的线程都关闭。

首先考虑解决内存问题。因为Java的内存泄漏问题往往跟HashMap相关,所以HashMap可能是一个突破口,而这么大量的内存泄漏,很可能会是某些HashMap存放过多已经没用的对象造成的(如果是这个原因,那改为用WeakHashMap就可以解决问题了)。我从HashMap和 LinkedHashMap派生了子类,其中加入了数据量检查的信息,把系统中所有构造HashMap和LinkedHashMap的地方都改为使用这些子类。但是运行后发现系统中构造的HashMap只有十几个,而且每个指向的对象也不多,只有少数几个是接近1千的,而仔细检查代码后,发现这几个指向对象较多的HashMap也不会造成太大的内存泄漏问题。

这样我的思路就中断了,只好求助于工具来寻找疑点。因为发现CPU占用到100%一段时间后,程序就呈死机状态没法运行下去了,内存问题也就无从检测,所以就决定先检查CPU,用Eclipse的profiler插件来查。如图:

http://download.smth.edu.cn/pcdownload.php?fid=6450

图1 Eclipse的profiler插件的Threads View

因为问题出在某个模块启动之后,所以这时先把统计功能关闭可以使结果更有意义(如果全部采样,初始化阶段的资源消耗对结果影响很大),也可以加快程序启动。等问题模块启动后,开始启动统计功能,此时点击Threads View中某个线程,在Thread method View中就会显示该线程中各调用的方法所运行的时间。

图2 点击Threads View中的某个Thread,Thread methods View中就会显示相关的内容

按Total time排序查看就可以找到哪些方法是占用CPU最多的。

图3 按Total time排序查看Thread methods的数据

但是当时我犯了非常严重的错误,以前在filter中把系统类(例如java.*、sun.*等等)过滤了,忘了取消,结果查出来的结果非常莫名其妙,最占用CPU的函数(达30%多)居然只有一个long型的赋值语句!这是在profiler的属性中设置的,如图:

图4 Eclipse的profiler插件的运行属性设置

后来想起有这个问题,把fliter重新设好,检测结果就正常了,不过没有什么有价值的线索,占用CPU较多的都是java系统包的方法(后来查出问题后回想才发现这里其实是有线索的)。于是就怀疑是内存快用光时JVM的内存管理模块会大量占用CPU,所以导致java程序的CPU占有率偏高(现在还不知道是否确实如此),另一方面,这些检测工具本身就大量消耗系统资源,也是导致CPU占用为100%的主要原因。于是,又改为用Borland Optimizeit Suite检查内存问题了。

先启动Optimizeit Suite中的profiler模块:

图5 启动Profiler模块

图6 在Profiler主界面中添加新Setting

注意使用Borland Optimizeit Suite时,jar中要把main class设好,classpath中要把所有用到的外部jar包全部输入,否则无法启动和正常运行。这个工具不太友好,无法启动时输出信息很不明确,所以最好自己先检查清楚了。

图7 在Profiler的Setting中设置main class和classpath

一般测试时都有运行多次,当发现某些包跟问题无关时,可以把它们过滤掉,这样最后得到的结果更容易阅读。

图8 设置filter

用Profiler启动程序。运行到问题模块后,点击工具栏最右边的按钮,标记当时各对象占用内存的情况。

图9 标记当前内存情况(图中红柱上的黑线)

点击工具栏上像感叹号的按钮,选择显示的内容。对这个问题,我觉得show size是最重要的。

http://download.smth.edu.cn/pcdownload.php?fid=6449

图10 设置显示的内容

再运行一段时间后,按Size diff排序。发现占用内存比较多的了后,双击该行,打开该对象的详细信息(目前bug已经改好,我只是随便点一个类作为示范)。这时展开整棵树,就可以看到该类所有实例分别是在什么方法中生成的(如果跟踪CPU信息,也有这种资源分配树显示)。

图11 某个类的所有对象的构造位置分布

因为占用内存最多的往往是系统基本数据类型,例如char,int[]等等,所以需要人工去判断哪些类是真正的疑点。我发现占用新申请内存排名第 20-30之间有几个类的对象数是完全相同的,而且持续同步增长,估计是某些线程中重复构造对象造成的,比其他以不规则速度增长的类更加可疑,于是就仔细检查了这几个的类的详细信息,发现果然是来自同一个函数。再仔细看代码,就找到内存泄漏的原因,原来是某行代码写错了,不断构造新的button。而保存这些button是用Vector!说明我最早使用的检查HashMap的思路是对的。可惜只是从自己的编程习惯出发,没有考虑到Vector,否则问题就更容易发现了。

不过当时还曾经检测到一个突变过程,至今没有想明白原因。在没有任何人对该计算机进行操作的情况下,CPU和内存占有率在某个时刻同时暴增。如图:

图12 CPU占用率突变

http://download.smth.edu.cn/pcdownload.php?fid=6457

图13 内存占用突变

欢迎知道原因的朋友回复,:)
分享到:
评论

相关推荐

    JAVA内存泄漏分析工具

    Java内存泄漏分析是一个关键的系统优化任务,尤其是在大型企业级应用中,长期运行的系统可能会因为内存泄漏导致性能下降甚至服务中断。"JAVA内存泄漏分析工具"正是一款用于解决此类问题的专业工具,它能帮助开发者...

    java内存泄漏分析工具

    本篇将详细探讨Java内存泄漏及其相关的分析工具。 内存泄漏是指程序在申请内存后,无法释放已不再使用的内存空间。在Java中,由于垃圾收集器的存在,程序员通常不必手动管理内存。然而,如果存在某些情况使得对象...

    如何解决Java内存泄漏

    ### 如何解决Java内存泄漏 #### 1. 背景 Java凭借其垃圾回收机制大大简化了内存管理,使得开发者无需手动管理内存的释放,从而提升了开发效率。然而,这种自动化管理也可能成为一把双刃剑,特别是当开发人员忽视...

    java内存泄露分析工具 eclipse3.5插件

    标题提到的"java内存泄露分析工具 eclipse3.5插件"正是指Eclipse Memory Analyzer与Eclipse IDE 3.5版本的集成。 Eclipse Memory Analyzer(MAT)通过分析heap dump文件来识别可能的内存泄露。Heap dump是Java...

    java内存分析-内存泄露问题.rar

    本文将深入探讨Java内存分析和内存泄露问题。 首先,我们需要了解Java内存模型的基础。Java内存主要分为三个区域:堆(Heap)、栈(Stack)和方法区(Method Area)。堆用于存储对象实例,栈用于存储方法调用及局部...

    java内存泄露、溢出检查方法和工具

    本文将深入探讨如何检测和分析Java内存泄露与溢出,并介绍一种常用的工具——Memory Analyzer(MAT)。 首先,理解内存泄露的概念至关重要。在Java中,内存泄露通常发生在对象不再被程序使用但仍然保持在内存中,...

    java 内存泄露分析流程

    总的来说,Java内存泄露分析是一个涉及多方面知识的过程,需要结合理论和实践来有效定位和解决问题。通过对内存模型、垃圾收集机制的理解,以及利用各种工具和技巧,我们可以有效地预防和解决内存泄露,确保系统的...

    基于Java的内存泄露分析及定位

    Java内存管理是一个关键的议题,...总的来说,理解Java内存管理和垃圾收集机制,以及如何使用工具进行分析和定位,是优化Java应用程序性能、避免内存泄漏的关键。通过有效的内存管理,可以确保程序高效且稳定地运行。

    MAT-Memory Analyzer Tool Java内存泄漏分析工具1.5

    MAT 是一个开源的java内存分析工具,能够快速的分析dump文件,可以直观的看到各个对象在内存占用的量大小,以及类实例的数量,对象之间的引用关系,找出对象的GC Roots相关的信息,此外还能生成内存泄露报表,疑似...

    java内存泄露定位与分析[整理].pdf

    Java内存泄露定位与分析是Java开发中的一项重要任务,尤其在企业级应用系统中,内存管理的优化直接关系到系统的稳定性和性能。当系统出现内存泄露时,可能导致应用程序响应变慢,甚至出现 Out Of Memory (OOM) 错误...

    java内存泄漏解决

    ### Java内存泄漏解决方案详解 #### 一、Java内存泄漏概述 在Java开发过程中,经常会遇到内存泄漏的问题,尤其是在长时间运行的应用程序中更为常见。本文将详细介绍如何解决Java内存泄漏问题,帮助开发者更好地...

    jProfiler7 java内存分析 linux版本

    本篇文章将详细探讨jProfiler7在Java内存分析上的核心功能、使用方法以及在Linux环境中的配置和应用。 1. **内存分析概述** - 内存分析是识别和解决Java应用程序中的内存泄漏、过度对象创建和内存消耗过高问题的...

    java内存泄露定位与分析.pdf

    java内存泄露定位与分析

    java内存泄漏问题追踪

    Java内存泄漏问题追踪 在Java编程中,内存泄漏是一个严重的问题,它会导致程序性能下降,甚至可能导致应用程序崩溃。内存泄漏是指程序在申请内存后,无法释放已不再使用的内存空间,随着时间推移,系统可用内存逐渐...

    JAVA内存分析 - V1.0.0.zip

    "mat" 指的是Eclipse Memory Analyzer Tool,这是一款强大的Java内存分析工具,特别适合于分析Java应用的内存泄漏和过大对象的问题。MAT可以通过heap dump文件对JVM的内存状态进行深度分析,提供详细的内存占用报告...

    Java内存泄露_JVM监控工具介绍

    "Java内存泄露_JVM监控工具介绍" Java内存泄露是Java开发中常见的一种问题,发生内存泄露可能会导致Java应用程序崩溃或性能下降。在Java中,内存泄露的原因非常多样,例如,静态变量、循环引用、数据库连接池、...

    Java内存泄露及内存无法回收解决方案

    4. Native内存泄漏:Java应用可能使用JNI(Java Native Interface)与C/C++代码交互,若C/C++部分存在内存泄漏,也会间接导致Java内存泄漏。 解决内存泄漏的方法主要包括: 1. 使用工具监控:通过工具如VisualVM、...

    Java内存泄露检测

    Java内存泄露检测是Java开发中一个关键的议题,因为它直接影响到程序的稳定性和资源效率。内存泄露是指程序中已分配的内存无法被正确地释放,从而导致系统资源的浪费和可能导致程序性能下降甚至崩溃。 首先,理解...

Global site tag (gtag.js) - Google Analytics