`
dato0123
  • 浏览: 970314 次
文章分类
社区版块
存档分类
最新评论

一次内存泄露问题的排查

 
阅读更多
系统对外提供的Solr查询接口,在来自外部调用的压力加大之后,就会出现solr查询报Read Timed Out的异常,从表面现象上看是此时solr核压力过大,无法响应过多的查询请求。

但实际上此时并发查询压力并不是很大,那么为何solr核会无法及时响应查询请求呢?首先用top查看了下load average,也是很低,也佐证了系统本身压力并不大。

然后,用jstack –l <pid>查看那些cpu使用率过高的线程,发现全都是GC线程,说明GC过于频繁,而且耗时过长,导致应用线程被挂起,无法响应客户端发来的请求,这种情况就应该是有存在内存泄露的问题咯。

于是,就用jmap将进程的堆转储文件dump出来到heap.bin文件中

JMap -dump:format=b,file=/tmp/heap.bin <pid>

然后用Eclipse Memory AnalyzerMAT)打开堆转储文件进行分析

通常我们都会采用下面的三步曲来分析内存泄露问题:

首先,对问题发生时刻的系统内存状态获取一个整体印象。

第二步,找到最有可能导致内存泄露的元凶,通常也就是消耗内存最多的对象

接下来,进一步去查看这个内存消耗大户的具体情况,看看是否有什么异常的行为。

下面将用一个基本的例子来展示如何采用三步曲来查看生产的分析报告。

查看报告之一:内存消耗的整体状况

如上图所示,在报告上最醒目的就是一张简洁明了的饼图,从图上我们可以清晰地看到一个可疑对象消耗了系统75%的内存。

现在,让我们开始真正的寻找内存泄露之旅,点击“Leak Suspects”链接,可以看到如下图所示对可疑对象的详细分析报告。

我们查看下从GC根元素到内存消耗聚集点的最短路径

我们可以很清楚的看到整个引用链,内存聚集点是一个拥有大量对象的列表,如果你对代码比较熟悉的话,相信这些信息应该能给你提供一些找到内存泄露的思路了。

接下来,我们再继续看看,这个对象集合里到底存放了什么,为什么会消耗掉如此多的内存。

在这张图上,我们可以清楚的看到,这个列表中保存了大量HashMap对象的引用,就是它导致的内存泄露。

至此,我们已经拥有了足够的信息去寻找泄露点,回到代码中就发现,List没有clear或者设置为null,导致其包含的强引用的各个HashMap没有得到释放。至此,问题得到解决。

下面我们来继续深入研究java的内存泄露问题。Java的一个重要优点就是通过垃圾收集器(Garbage CollectionGC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GCJVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。

随着越来越多的服务器程序采用Java技术,例如JSPServletEJB等,服务器程序往往长期运行。另外,在很多嵌入式系统中,内存的总量非常有限。内存泄露问题也就变得十分关键,即使每次运行少量泄漏,长期运行之后,系统也是面临崩溃的危险。

为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对象都在堆(Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。

监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

为了更好理解GC的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象(连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左边程序运行到第6行的示意图。

Java使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。这种方式的优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度行低(很难处理循环引用的问题),但执行效率很高。

下面,我们就可以描述什么是内存泄漏。在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。

通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。

因此,通过以上分析,我们知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。

对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个Vector中,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null



分享到:
评论

相关推荐

    Tomcat内存泄露问题排查

    单次内存泄露的影响可能较小,但随着时间积累,这种行为会导致系统可用内存逐渐耗尽,最终引发性能问题甚至系统崩溃。 #### 使用MAT进行内存分析 - **工具选择**:Memory Analyzer Tool (MAT)是一款基于Eclipse的...

    electron-vue开发环境内存泄漏问题汇总.docx

    在 Electron v4.0.0 中,发现每次保存并不是都会消失一个新的进程,经过多次测试后,发现问题出在保存完后,主进程未生成胜利,然后再次保存,于是进程创建了多次,但是只杀死了最开头的进程,造成内存泄漏。...

    JProfiler对应用服务器内存泄漏问题诊断解决方案.docx

    这种问题会导致应用服务器极为不稳定,几乎每两天重新启动一次,有时甚至每天重新启动一次。解决这个问题是非常重要的。 二、问题描述 某个大型项目(Use Case 超过 300 个),在项目上线后,其 Web 应用服务器...

    一次 Java 内存泄漏的排查解决过程详解

    Java 内存泄漏排查解决过程详解 Java 内存泄漏是一种常见的错误,会导致服务不可用或性能下降。本文将详细介绍一次 Java 内存泄漏的排查解决过程,通过示例代码和实际案例,帮助读者更好地理解和排查 Java 内存泄漏...

    android项目内存泄露排查实用.pdf

    我们的项目中,在 Android 2.3.5 平台上,每次 Typeface.createFromAsset 的时候就会泄漏一次 ttf 字体文件操作的 IO 流,因为项目界面统一使用了这个自定义字体,所以很快就可以泄漏几百 MB native 空间,导致进程...

    记一次django内存异常排查及解决方法

    本文作者分享了一次处理此类问题的经验。 首先,当遇到Django应用的内存问题时,我们需要了解内存异常的一般来源。通常,内存异常是由于代码中存在内存泄漏、过度使用内存或数据结构管理不当造成的。在Python中,...

    JAVA内存泄漏分析工具

    内存泄漏是程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄漏可能看似无足轻重,但随着时间推移,这些未被释放的内存会不断积累,最终可能导致系统资源耗尽。而内存溢出则是程序请求的内存超过了系统所...

    ie内存泄漏检测软件

    内存泄漏,简单来说,是指程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄漏可能影响不大,但随着时间推移,多次累积会导致系统可用内存越来越少,进而引发性能下降甚至系统崩溃。在IE浏览器中,由于其...

    nodeJs内存泄漏问题详解

    之前一次偶然机会发现,react 在server渲染时,当NODE_ENV != production时,会导致内存泄漏。具体issues: https://github.com/facebook/react/issues/7406 。随着node,react同构等技术地广泛运用,node端内存泄漏...

    内存溢出排查手册.pdf

    #### 内存泄漏排查及代码优化 1. **使用Arthas监控内存**: - 查看所有Java进程信息:`jcmd -l` - 手动触发垃圾收集(FGC):`jcmd &lt;pid&gt; GC.run`(注:生产环境中避免在高峰期手动GC) - 通过Arthas监控仪表盘...

    介绍使用脚本判断内存泄漏的简便方法

    内存泄漏是程序设计中的一个常见问题,尤其在长时间运行的应用程序中更为显著。对于Linux等UNIX系统而言,内存泄漏可能导致系统资源被逐渐耗尽,最终影响到系统的稳定性和响应速度。因此,及时发现并解决内存泄漏...

    JAVA内存溢出问题总结

    内存溢出问题可以从容器和程序类两个方面进行排查,容器问题可以调整容器参数来解决。 从程序类方面来说,内存溢出的原因有很多,以下是常见的几种: 1、查询数据库的时候递归循环了,应尽量使用精简的关联 SQL ...

    自己编写的windowsCE6.0内存泄漏检测工具

    内存泄漏是指程序在申请内存后,无法释放已申请的内存空间,一次小的内存泄漏可能不会立即引发问题,但随着时间推移,累积的内存泄漏会消耗掉系统可用资源,最终可能导致系统性能下降甚至崩溃。因此,内存泄漏检测是...

    【JAVA WEB实用技巧与优化方案】如何排查JVM线程和内存相关问题

    在Java Web开发中,性能优化和问题排查是关键任务,特别是涉及到JVM(Java Virtual Machine)线程和内存的问题。本文将深入探讨如何利用“javacore”分析工具以及IBM Thread and Monitor Dump Analyzer来有效诊断和...

    【JavaScript源代码】NodeJs内存占用过高的排查实战记录.docx

    本文记录了一次线上容器因内存占用过高而自动扩容的过程。该事件发生在一个部署于腾讯TAF平台的Node.js服务上,该服务主要负责接口查询及使用socket.io进行通信。尽管服务功能简单且并未遇到大流量或高并发的压力,...

    symbian内存检查工具HookLogger

    它通过挂钩系统级别的内存分配和释放函数,记录下每一次内存操作的详细信息,帮助开发者追踪和定位潜在的内存泄露问题。内存泄露是指程序在申请内存后,无法释放已不再使用的内存空间,久而久之会消耗掉系统可用内存...

    C程序内存泄漏智能化检测方法.pdf

    它监视程序的内存使用情况,记录下每一次内存分配与释放的操作,从而检测出未释放的内存。但是,动态检测依赖于详尽的测试用例,而创建全面的测试用例往往成本高昂。此外,测试用例的覆盖率限制了动态检测方法的检测...

    提高Java开发质量之内存泄露

    3. **维护成本增加**:内存泄露往往难以追踪,需要花费大量时间排查问题,增加了维护成本。 #### 三、单元测试的重要性 **单元测试的作用:** 单元测试是在软件开发中对最小可测试单元进行检查和验证的一种测试...

    loadrunner分析内存泄露

    在软件开发过程中,内存泄露是一个常见的问题,它可能导致应用程序性能下降甚至崩溃。本文将详细介绍如何使用LoadRunner工具来定位和解决内存泄露问题。 #### 二、问题现象 在进行性能测试时,测试人员注意到随着...

Global site tag (gtag.js) - Google Analytics