自从公司开始将java作为主要开发语言后,C++与java的混合应用日趋增多。 java与C++的通信主要也是使用JNI来完成,这并没有什么问题。对于这样的混合应用项目来说,最大的噩梦莫过于memory leak诊断了。由于Java的内存管理模式与C++有很大的区别,所以对这样的项目进行调试时,首先要区分是Java代码的memory leak还是C++代码的memory leak。对于内存诊断来说,我们需要先了解一些指标含义和工具的使用,这样才能做到有理有据。

指标:

memory(working set): MSDN的说明-The working set of a process is the set of pages in the virtual address space of the process that are currently resident in physical memory. The working set contains only pageable memory allocations; nonpageable memory allocations such as Address Windowing Extensions (AWE) or large page allocations are not included in the working set.

一个进程的专用工作集指的是当前进程常驻于物理内存的虚拟内存页面集。它只包含可以被分页的内存区;那些不能被分页的内存区如AWE(一种应用程序可以直接操纵大于4G物理内存的技术)或是LPA(主要用于服务器的大物理内存上,一般对于64位的系统比较有用)不会被包括在专用工作集中。

virtual bytes: 当前进程所使用的虚拟内存大小。这个指标包含所有的内存页面文件,如在磁盘交换区中的页面文件,加载的库文件等。

private bytes:当前进程已经分配的私有内存的大小,不包含共享给其他进程使用的内存。

在一般情况下,如果你的应用程序需要的内存不多,并且比较活跃,内存泄漏比较明显,那么通过监视working set就可以看出是否有内存泄漏。但是如果应用程序比较复杂,模块较多而且需要的内存在不同时刻变化比较大,那么单纯根据working set是看不出来问题的,因为一些页面文件会在某一时刻被交换的磁盘缓冲区中。那么,我们就需要去分析virtual bytes和private bytes这两个指标。但这也不是绝对准确的,因为有些时候比如内存碎片比较多,而应用程序经常请求大块连续的内存,也会造成virtual bytes增加的情况。所以,在实际环境中我们还需要了解应用程序的内存使用特点来确定是否有memory leak问题。

介绍完了指标,下面介绍一下工具:

对于Java程序来说,比较好的监视内存的工具是jvirsualVM。这个工具是java自带的,它可以监视本地或远程的java应用程序,也可以监视系统服务这样的程序。你可以在JDK的bin目录下找到。其他的还有比如jconsole, eclipse的MAT等。

对于C++的程序来说,那工具可就多了。这里我主要用的是IIS Debug Diagnostics Tool,这个原本是用于IIS应用程序的诊断工具,在监视系统服务这样的应用上还是很方便的。同时它可以进行自动的memory leak分析并生成报表。对于memory leak的诊断很有帮助。当然,我还用到了vmmap和rammap两个应用程序。这两个程序原先是system internal那个作者开发的,现在已经收归微软门下了。这两个文件一个用于查看进程的虚拟内存分配情况,而另一个拥有查看物理内存的时候情况。最后一个工具就是process hacker,它能帮助我们更详细的了解进程的内存分配,句柄分配,模块加载,线程数目等。当然processexplorer也可以做相同的工作,但是如果你要查看内存块的内容时,还需要windbg的配合。

基本上我们需要的东西已经都有了,那么接下来就是真正的调试之旅了。这里我先介绍一下我要调试的程序是基于tomcat的企业级备份服务,java的工作是基于c++模块上来做的统计和管理工作。对于这样一个应用,memory leak是一个很头疼的问题,因为基本上不能通过调试来解决。那么如果出现了memory leak,我们先要区分出来是java代码还是c++代码。因为java是具有垃圾回收机制的语言,所以memory leak比较不好检查。那么对于java来说什么样的情况才是memory leak呢? 某个对象不能被回收,就是一个leak,比如object被放在了一个singleton的列表中,只要这个singleton没有被释放,那么这个object永远存在于内存中。对于tomcat程序来说,我们需要先配置几个参数用于jvisualvm的监视:

-Dcom.sun.management.jmxremote.port=8086  
-Dcom.sun.management.jmxremote.ssl=false  
-Dcom.sun.management.jmxremote.authenticate=false

这样就可以在jvisualvm里面建立一个JMX的连接用于tomcat,然后我们需要在不同的时间点去打heap dump,然后通过比对两个时期的对象变化来检查是否发生memory leak。这里需要你对你的应用程序对象分配比较了解才行。在java这块对于内存来说需要关注的主要是heap和PermGen,heap是new对象要用的,而PermGen是存放class和meta data的内容。

经过不懈的努力,最终发现我们程序中对http connection没有设置超时,从而导致在持久化连接模式下,很多访问web service的线程会hang住。整完了Java部分的memory leak问题,就该整C++模块的问题了。通过一段时间的观察发现,working set的大小会在某一时刻降下来,但是virtual bytes和private bytes是阶段性的上涨。这说明可能是某段代码请求大块内存而产生的,但是这并不能证明是leak。而且这段代码也不一定是在C++中。为了找到问题是在C++中还是Java中,我需要对java的内存使用情况进行跟踪(这里使用到了process hacker的内存检查功能)。

最终我发现java部分的虚拟内存没有变化,一直维持在我们设定的最大内存大小之内。这样我们就可以从C++这边开始工作了。先用vmmap查看一下内存的使用情况,发现在一个地方内存读写量非常大,这证明有程序非常的密集访问某块内存。接下来就是使用Debug Diagnostics Tool对Tomcat的service进行跟踪了. 先创建一个memory leak的规则,然后开始跟踪。在一段时间后,内存出现比较大的变化后,选择memory dump并进行自动分析。你会得到一个大概的内存分析报告,并且报告会给出可能的memory leak模块。根据这个信息你就有选择性的去检查某个模块的代码来确定是否真的发现leak了。

最终发现问题是在加密解密函数出来问题,在一个函数中malloc了一块内存用于存储字符串,然后将它加密完后,拷贝到新的加密完的内存中就直接返回了,没有去free它。而是在返回的代码后面去free。这样就造成了每一次成功的加密就会泄漏一块内存,因为加密函数只在特定的时候被调用,所以内存成阶段性的上涨。

到这里,整个诊断memory leak的过程也就结束了。我的感受是搞这种问题既要有技术也要有运气^_^ ! (以上的图是现找的,不说明真实情况,所以大家看看知道个样子就可以了)

涉及到的工具通过google都可以找到。

将想法付诸于实践,借此来影响他人是一个人存在的真正价值

原来问题在这里-我的memory leak诊断历程的更多相关文章

  1. Android 内存管理 &Memory Leak & OOM 分析

    转载博客:http://blog.csdn.net/vshuang/article/details/39647167 1.Android 进程管理&内存 Android主要应用在嵌入式设备当中 ...

  2. quartz集群报错but has failed to stop it. This is very likely to create a memory leak.

    quartz集群报错but has failed to stop it. This is very likely to create a memory leak. 在一台配置1核2G内存的阿里云服务器 ...

  3. 山东省第七届ACM省赛------Memory Leak

    Memory Leak Time Limit: 2000MS Memory limit: 131072K 题目描述 Memory Leak is a well-known kind of bug in ...

  4. caching redirect views leads to memory leak (Spring 3.1)

    在Spring 3.1以及以下版本使用org.springframework.web.servlet.view.UrlBasedViewResolver + cache(如下配置),在会出现任意种re ...

  5. 一则JVM memory leak解决的过程

    起因是我们的集群应用(3台机器)新版本测试过程中,一般的JVM内存占用 都在1G左右, 但在运行了一段时间后,慢慢升到了4G, 这是一个明显不正常的现象. 定位 过程: 1.先在该机器上按照步骤尝试重 ...

  6. Linux C/C++ Memory Leak Detection Tool

    目录 . 内存使用情况分析 . 内存泄漏(memory leak) . Valgrind使用 1. 内存使用情况分析 0x1: 系统总内存的分析 可以从proc目录下的meminfo文件了解到当前系统 ...

  7. SilverLight - Memory Leak

    There is a memory leak issue in current silverlight project. It occurs in the search function: the m ...

  8. A memory leak issue with WPF Command Binding

    Background In our application, we have a screen which hosts several tabs. In each tab, it contains a ...

  9. quartzScheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak解决

    01-Jul-2016 07:24:20.218 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 80 ...

随机推荐

  1. Linux下,部署多个Tomcat

    1.复制一个已有的tomcat,并重命名: 2.打开tomcat--conf--server.xml:[共修改3处配置] <Server port="8005" shutdo ...

  2. Office 2013“永久激活信息”备份

    Office 2013“永久激活信息”备份还原简明教程及成功恢复的注意事项Office 2013永久激活后及时备份激活信息可以保证重装后快速激活.网上也有流行的各种备份工具,虽然操作简单,但是如果不理 ...

  3. sprintf在51单片机中的使用

    sprintf在51单片机中的使用 unsigned char ch20_str[4]; unsigned char ch2o_m_str[6]; ch2o = 123; ch2o_m = 23456 ...

  4. python基础16 ----面向对象程序设计二

    一.继承与派生 1.继承的定义:继承是一种创建新类的方式,即在类中提取共同的部分创建出一个类,这样的类称为父类,也可称为基类和超类,新建的类称为派生类或子类. 2.单继承:就相当于子类继承了一个父类. ...

  5. mail

    mail.php  <?php require_once('class.phpmailer.php'); $mail = new PHPMailer(); //实例化 $mail->IsS ...

  6. 模仿jquery框架源码 -成熟---选择器

    <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...

  7. iOS 在视图控制器里面判断 应用程序的前台 后台切换 UIViewController

    1.时机  用户点击home 键  应用退到后台 再次点击进入前台  在UIViewController里面 控制器如何获取相关的事件? 2.需求 (1)NSTimer   在应用程序进入后台 10秒 ...

  8. css 行内元素 块元素 替换元素 非替换元素 以及这些元素的width height margin padding 特性

    一.各种元素的width height margin padding 特性(具体css元素的分来参看二) 1.块级元素 width. height. margin的四个方向. padding的四个方向 ...

  9. 一步一步带你分析 requirejs

    详细源代码一共就2000多行,来看我这篇分析的同学应该都下载下来了,好了,话不多说,开始: 代码的开头就出现3个全局变量: requirejs, require, define var require ...

  10. LINQ 学习路程 -- 查询操作 Select, SelectMany

    IList<Student> studentList = new List<Student>() { , StudentName = "John" }, , ...