专栏首页一个程序员的修炼之路vmmap分析内存泄露问题

vmmap分析内存泄露问题

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

vmmap介绍

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

  • Free: 图中显示137434599232K,是不是被吓到了。这个一般是指虚拟地址空间。每个进程都有自己的虚拟地址空间,比如32位的一般为4G,其中2G是内核地址空间, 2GB用户态地址空间;64位理论上为2^64个字节,实际上没那么大,按照MSDN的描述64位的Windows用户态可使用地址空间为128TB
  • Heap: 这个主要就是指我们通过C/C++的malloc, new;以及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 <iostream>
#include <chrono>
#include <thread>
#include <windows.h>

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或者一些限制。但是通过如上的方法和思想,也许能够协助你找到内存泄露点,至少可以起到辅助的作用。

相关阅读

  1. <<Windows内存泄露分析之DebugDialog>>
  2. <<Windows程序内存泄漏(Memory Leak)分析之Windbg>>
  3. <<Windows程序内存泄漏(Memory Leak)分析之UMDH>>

本文分享自微信公众号 - 一个程序员的修炼之路(CoderStudyShare),作者:河边一枝柳

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-06-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • K8S 问题排查:cgroup 内存泄露问题

    这篇文章的全称应该叫:[在某些内核版本上,cgroup 的 kmem account 特性有内存泄露问题],如果你遇到过 pod 的 cannot alloca...

    YP小站
  • Windows内存泄露分析之DebugDialog

    Windows中内存泄露的文章本人已经写过两篇<<Windows程序内存泄漏(Memory Leak)分析之UMDH>>和<<Windows程序内存泄漏(Mem...

    河边一枝柳
  • Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

    例:Object.finallize()、 Windows.dispose()、 System.gc()

    马士兵的朋友圈
  • 解决ThreadLocal的内存泄露问题

    ThreadLocal的内存泄露问题   根据上面Entry方法的源码,我们知道ThreadLocalMap是使用ThreadLocal的弱引用作为Key的。...

    好派笔记
  • 小题大做 | Handler内存泄露全面分析

    "内部类持有了外部类的引用,也就是Hanlder持有了Activity的引用,从而导致无法被回收呗。"

    码上积木
  • APNS开源包的内存泄露问题

    APNS(全称:Apple Push Notification Service),主要是用于往苹果设备推送push消息通知!

    用户7676729
  • pwnable.tw刷题之dubblesort

    前言 上一篇中我介绍了phttp://www.freebuf.com/articles/others-articles/134271.htmlwnable.tw...

    FB客服
  • Windows程序内存泄漏(Memory Leak)分析之Windbg

    之前本人写了一篇<<Windows程序内存泄漏(Memory Leak)分析之UMDH>>。这种方法有一定的局限性:

    河边一枝柳
  • 利用S_MEMORY_INSPECTOR分析内存泄漏问题

    我在批量生成service order时,report运行几个小时后,遇到out of memory exception:

    Jerry Wang
  • 利用S_MEMORY_INSPECTOR分析内存泄漏问题

    我在批量生成service order时,report运行几个小时后,遇到out of memory exception:

    Jerry Wang
  • 利用S_MEMORY_INSPECTOR分析内存泄漏问题

    我在批量生成service order时,report运行几个小时后,遇到out of memory exception:

    Jerry Wang
  • 记一次 JAVA 的内存泄露分析

    前不久,上线了一个新项目,这个项目是一个压测系统,可以简单的看做通过回放词表(http请求数据),不断地向服务发送请求,以达到压测服务的目的。在测试过程中,一切...

    芋道源码
  • axios 浏览器内存泄露问题解决

    业务页面,频繁切换下一条,内存飙涨,导致卡顿,之前怀疑是音频播放器的锅,修改后问题依旧,于是排查网络请求。

    JadePeng
  • 一个 Vue 页面的内存泄露分析

    什么是内存泄露?内存泄露是指new了一块内存,但无法被释放或者被垃圾回收。new了一个对象之后,它申请占用了一块堆内存,当把这个对象指针置为null时或者离开作...

    用户1272076
  • 记一次调试python内存泄露的问题

    这两天由于公司需要, 自己编写了一个用于接收dicom文件(医学图像文件)的server. 经过各种coding-debuging-coding-debugi...

    小小科
  • spring boot + embed tomcat + standalone jar的内存泄露问题

    前一阵遇到了一个很坑的内存泄露问题,记录于此: 有个项目采用spring cloud重构后,部署到线上(其中有一个接口,大概每天调用量在1千万次左右),发现za...

    菩提树下的杨过
  • React: 内存泄露常见问题解决方案

    Can't perform a React state update on an unmounted component. This is a no-op, ...

    西南_张家辉
  • 一次 Netty 堆外内存泄露问题排查

    最近在做一个基于 websocket 的长连中间件,服务端使用实现了 socket.io 协议(基于websocket协议,提供长轮询降级能力) 的 netty...

    涤生
  • PWNCTF部分复现

    根据readData和writedata函数的逻辑发现数组是char [22][12],主要是判断越界的if语句有逻辑漏洞

    安恒网络空间安全讲武堂

扫码关注云+社区

领取腾讯云代金券