原来问题在这里-我的memory leak诊断历程
自从公司开始将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诊断历程的更多相关文章
- Android 内存管理 &Memory Leak & OOM 分析
转载博客:http://blog.csdn.net/vshuang/article/details/39647167 1.Android 进程管理&内存 Android主要应用在嵌入式设备当中 ...
- 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内存的阿里云服务器 ...
- 山东省第七届ACM省赛------Memory Leak
Memory Leak Time Limit: 2000MS Memory limit: 131072K 题目描述 Memory Leak is a well-known kind of bug in ...
- caching redirect views leads to memory leak (Spring 3.1)
在Spring 3.1以及以下版本使用org.springframework.web.servlet.view.UrlBasedViewResolver + cache(如下配置),在会出现任意种re ...
- 一则JVM memory leak解决的过程
起因是我们的集群应用(3台机器)新版本测试过程中,一般的JVM内存占用 都在1G左右, 但在运行了一段时间后,慢慢升到了4G, 这是一个明显不正常的现象. 定位 过程: 1.先在该机器上按照步骤尝试重 ...
- Linux C/C++ Memory Leak Detection Tool
目录 . 内存使用情况分析 . 内存泄漏(memory leak) . Valgrind使用 1. 内存使用情况分析 0x1: 系统总内存的分析 可以从proc目录下的meminfo文件了解到当前系统 ...
- SilverLight - Memory Leak
There is a memory leak issue in current silverlight project. It occurs in the search function: the m ...
- 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 ...
- 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 ...
随机推荐
- java 基于tomcat的数据源案例
1.在context中定义数据源 <?xml version="1.0" encoding="UTF-8"?> <Context path=& ...
- 今天在网上查看了一个socket程序,运行的时候一直报错,经过队友解决?
1.首先是问题代码ip_port = ('192.168.12.2',8001)2.上边的代码本身没有问题,但是必须经过修改自己本机的局域网IP地址才能顺利链接请参考上一篇blog的地址,查看本机的i ...
- 在函数中如何获取 线程对象、线程唯一ID
threading.current_thread() threading.current_thread().ident
- MySql四种隔离级别
什么是事务 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做. 事务的结束有 ...
- python __nonzero__方法
类的nonzero方法用于将类转换为布尔值.通常在用类进行判断和将类转换成布尔值时调用.比如语句if A: print 'foo'中就会调用A.nonzero()来判断.下面这个程序应该能帮助你理解n ...
- redis学习笔记 - Pipeline与事务
原文 Redis提供了5种数据结构,但除此之外,Redis还提供了注入慢查询分析,Redis Shell.Pipeline.事务.与Lua脚本.Bitmaps.HyperLogLog.PubSub.G ...
- 使用django开发一个博客
环境: MAC 10.10.5 Yosemite Python 3.73 Django 代码托管 github
- C#聚合运算方法
Aggregate 对集合值执行自定义聚合运算 Average 计算集合平均值 Count 对集合的元素惊醒计数,还可以仅对满足某一谓词函数的元素进行计数 LongCount 对大型集合中的元素进行计 ...
- Advanced GET 9.1 修正汉化版(免注册、页面加载、保存都正常)
http://www.55188.com/viewthread.php?tid=2846679 Advanced GET 9.1 修正汉化版(免注册.页面加载.保存都正常) 网上流传的很多GET9.1 ...
- 用cocos2d-html5做的消除类游戏《英雄爱消除》(1)——系统主菜单
系统主菜单如下图所示: 首先,介绍下这个主菜单,它包含了一个动画logo以及一个按钮选项,动画logo每隔1秒钟切换一张图片,点击相应的按钮选项会切换不同的游戏场景. 下面看下这个界面的源码: /** ...