当前位置:首页 > 单片机 > CPP开发者
[导读]vmmap是sysinternals工具集中的一个工具,主要用于分析一个进程的虚拟内存和物理内存的使用情况。更有效的是,可以通过对比两个不同时间的内存使用情况的Snapshot,来查找内存泄露问题。vmmap介绍当你用vmmap去查看一个正在运行的进程的时候。可以看到如下图,不同...

vmmapsysinternals工具集中的一个工具,主要用于分析一个进程的虚拟内存和物理内存的使用情况。更有效的是,可以通过对比两个不同时间的内存使用情况的Snapshot,来查找内存泄露问题。

vmmap介绍

当你用vmmap去查看一个正在运行的进程的时候。可以看到如下图,不同类型的内存使用采用不同的颜色标明。VMMap主要列举了以下几种类型的内存使用情况:

  • Free: 图中显示137434599232K,是不是被吓到了。这个一般是指虚拟地址空间。每个进程都有自己的虚拟地址空间,比如32位的一般为4G,其中2G是内核地址空间, 2GB用户态地址空间;64位理论上为2^64个字节,实际上没那么大,按照MSDN的描述64位的Windows用户态可使用地址空间为128TB

  • Heap: 这个主要就是指我们通过C/C 的mallocnew;以及HeapAlloc等申请的内存大小

  • Image: 比较好理解,一般指进程启动的运行文件,比如Exe或者加载的DLL文件。

  • Managed Heap: 这个一般指用C#编写代码使用的托管堆。比如一个程序可能是C#和C 均有实现,这个时候可以查看是不是托管堆占用的内存持续增高,那么就可以判断一般是C#部分托管堆使用有问题造成了泄露。

  • Mapped File: 主要是指内存映射文件,熟悉的同学应该知道,这也是常用的进程间通信的一种方式。

  • Private Data: 主要指通过VirtualAlloc申请的内存空间。这里也注意同Free主要是指已经使用的地址空间,而非已经Commit的内存。比如下图中,

  • Stack: 函数栈所使用的内存大小

  • Shareable: 主要是进程间可以共享的内存,但是后备存储器为RAM或者Paging File(一般是指虚拟内存page.sys)。

  • Page Table: 主要指内核中和该进程页表相关联的内存


对于其他的描述,本人本人主要介绍两种需要关注的:

  • Committed: 对于一个虚拟地址空间的使用,我们可以是申请地址空间,但不提交(commit),如果不提交,则不会占用真实的存储器空间(比如RAM或者Paging File),只有commit后才会使用物理内存(RAM或者Paging File)。那么VMMap这里所指的内存就是后备存储器为RAM, Paging File, 或者Mapped file。

  • Working Set: 一般内存有RAM,还有虚拟内存(page.sys),而根据内存的调度原理,并不是所有的内存都常驻RAM。Working Set就是主要指在RAM中所使用的内存。

VMMap分析内存泄露

笔者曾经有一次用过VMMap分析过内存泄露,但是最终问题并不是通过VMMap分析出来的,主要是因为当运行到比较长的时间的时候VMMap偶尔会出现崩溃的情况。但是VMMap确实可以辅助分析出内存泄露问题,笔者也是将这个方法分享给大家。


下面是一段便于读者理解Vmmap分析方法的样例。首先每隔10秒钟,申请10M内存,总共申请10次;然后每隔10秒释放1次内存,只释放5次。这样操作,可以简单模拟,一个程序在运行中既有正常的内存申请释放的场景,也有申请后却没有释放的场景,这样交错在一起,让问题更加逼近现实。这样也便于使用这种方法,在未来碰到问题的时候进行实战。

#include 
#include 
#include 
#include 
void HeapMemoryLeakSample() {
 const int iListSize = 10;
 char* pHeapList[iListSize];
 //Alloc 10 Heap STR_SIZE
 const int STR_SIZE = 10 * 1000 * 1000;
 for (int i = 0; i < iListSize; i ) {
  pHeapList[i] = new char [STR_SIZE];
  strcpy_s(pHeapList[i], STR_SIZE, "Alloc Memory");
  std::cout << pHeapList[i] << std::endl;
  std::this_thread::sleep_for (std::chrono::seconds(10));
 }
 //Free 5 Heap space
 for (int i = 0; i < iListSize; i ) {
  if (i % 2 == 0) {
   delete pHeapList[i];
   std::cout << "Free Memory" << std::endl;
   std::this_thread::sleep_for (std::chrono::seconds(10));
  }
 }
}
int main() {
 HeapMemoryLeakSample();
 while (true) {
  std::this_thread::sleep_for (std::chrono::seconds(10));
 }
 return 0;
}
接下来一起来查看是如何定位一个程序的内存泄露的。


第一步 配置好程序的位置,工作目录,以及符号文件目录:


第二步 当运行程序,首先看到整个VMMap界面。这个时候映入眼帘的好多好多数据,该看什么呢?首先对于一般的C 程序而言,堆的内存泄露使用是最常见,那么就先看下Heap部分的Committed大小是不是很大。比如本文的样例,发现已经有70M左右的大小。先锁定到溢出内存类型为Heap

第三步 个人认为查找内存泄露也需要一些技巧和常识的。比如程序刚启动不久的时候,申请的很多资源是全局的,或者伴随着整个进程的生命周期的,那么刚启动后的内存的增长一般可以忽略,不认为是内存泄露的原因。再大概程序运行一段时间后(根据自己程序实际情况而定),基本的伴随整个进程的生命周期的资源已经创建完毕。此时可以使用TimelineAddress部分的功能对照查看。

这个时候首先选择Heap(点击一下),那么Address部分将会显示Heap所占用的内存。然后当我们打开Timeline,选择特定的时间段区域,比如上图中选择区域为刚开始申请内存的部分,每隔10秒,增加申请10M内存。此时重要的是Address部分也会动态的展示这段时间的内存变化。

然后注意其中的内存使用比如000001B39E445000的内存被申请了,然后拉长时间线,发现很长时间还是存在在Address栏中,并且绿色,就说明一直没有被释放。

此时当你选中这个地址,再选择Heap Allocations,便可以看到其申请的大小为10000000, 双击打开后便可以查看到函数调用栈了。如下图所示便可以找到是在HeapMemoryLeakSample函数内调用了new,并且有行号提示(不过这里的行号提示不够精准,但是也不影响你去分析问题了)。

也可以不选择区间,而选个某个时间点,查看内存的状态。

第四步 如果很幸运,第三步已经找到问题了。第四步本来想说一说Call Stack的追踪的,比如通过申请的内存的Count或者Bytes来查找到可疑的内存泄露点的函数调用栈。可是笔者多次实验后均发现,数据对不上。比如下图的Count百分比和Bytes百分比之和均对不上100%。所以笔者也不会对此做过多的赘述,调试软件同样也是软件,也可能存在bug或者一些限制。但是通过如上的方法和思想,也许能够协助你找到内存泄露点,至少可以起到辅助的作用。

- EOF -

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭