C语言内存管理(三)
2011年04月15日
1-4 在Windows 下如何跟踪内存泄露
通过上面章节的学习,我们对内存泄露深恶痛绝,那么如何检查一个程序的内存泄露呢?先介绍一个最简单的方法就是使用VC 调式工具。首先我们来故意产生内存泄露。
1、我们创建一个memleak的支持MFC的工程,工程类型为 win32 Console Application,如图所示,并单击“OK”按钮。
2、在接下来的项目中我们选择“An application that supports MFC.”选择支持MFC的控制台程序。并单击“Finish”。
3、在接下来的界面中单击“ok”按钮完成工程创建。
我们修改memleak.cpp,程序如下:
#include "stdafx.h"
#include "memleak.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
//故意产生内存泄露的函数
void memleaktest()
{
char * szTemp= new char[1024];
szTemp=(char *)malloc(1024);
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr
F:\2008\07\prj\memleak\memleak.cpp(22) : {60} normal block at 0x00386F08, 1024 bytes long.
Data: CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
我们发现了VC探测出了内存泄露,此时我们点击行:
F:\2008\07\prj\memleak\memleak.cpp(22) : {60} normal block at 0x00386F08
则出现我们产生内存泄露的详细地方。
也许朋友会问,这是怎么实现的呢?我们不仿来分析以下代码,大家一定要注意如下代码
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
在debug状态下,我们使用new分配内存的时候,实际上会调用 DEBUG_NEW,该宏是怎么定义的呢?在Afx.h中我们找到了它的定义
//Afx.h
// Memory tracking allocation
void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
#define DEBUG_NEW new(THIS_FILE, __LINE__)
#if _MSC_VER >= 1200
void AFX_CDECL operator delete(void* p, LPCSTR lpszFileName, int nLine);
#endif
在Afxmem.cpp中我们找到了函数定义,如下:
//afxmem.cpp
void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine)
{
return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
}
#ifdef _DEBUG
void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)
{
#ifdef _AFX_NO_DEBUG_CRT
UNUSED_ALWAYS(nType);
UNUSED_ALWAYS(lpszFileName);
UNUSED_ALWAYS(nLine);
return ::operator new(nSize);
#else
void* pResult;
#ifdef _AFXDLL
_PNH pfnNewHandler = _pfnUninitialized;
#endif
for (;;)
{
pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);
if (pResult != NULL)
return pResult;
#ifdef _AFXDLL
if (pfnNewHandler == _pfnUninitialized)
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
pfnNewHandler = pState->m_pfnNewHandler;
}
if (pfnNewHandler == NULL || (*pfnNewHandler)(nSize) == 0)
break;
#else
if (_afxNewHandler == NULL || (*_afxNewHandler)(nSize) == 0)
break;
#endif
}
return pResult;
#endif
}
#endif //_DEBUG
鉴于本章的主旨,我们不再继续分析下去,不过我们知道了VC6.0 大抵是如何跟踪内存泄露的。
上面分析内存泄露的方法是常见手段,一般程序员都必须掌握的。下面再介绍一种将内存泄露的信息输出到日志文件的办法,该方法非常简单,如果再加上定时器的,则可以定时分析系统运行到现在存在有哪些内存没有释放,如何实现呢?我们不妨还是使用刚才故意产生内存泄露的例子。
1、 打开 stdafx.h 添加后的代码如下(黑体部分是我们需要添加的代码):
#if !defined(AFX_STDAFX_H__C6C9B115_8277_4302_914D_17F87E13978E__)
#define AFX_STDAFX_H__C6C9B115_8277_4302_914D_17F87E13978E__
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#include
#include // MFC core and standard components
#include // MFC extensions
#include // MFC support for Internet Explorer 4 Common Controls
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT
#include
#ifdef _DEBUG
//for memory leak check
#define _CRTDBG_MAP_ALLOC //使生成的内存dump包含内存块分配的具体代码为止
#include
#include
#endif
#endif
2、 在程序执行的开始,设置内存跟踪及设置输出文件
代码如下:
HANDLE hLogFile;//声明日志文件句柄
//允许检查内存泄露
_CrtSetDbgFlag( _CRTDBG_REPORT_FLAG);
{
int nRetCode = 0;
//创建日志文件
hLogFile = CreateFile("c:\\memleak.log", GENERIC_WRITE,
FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//将warn级别的内容都输出到文件(注意dump的报告级别即为warning)
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
//将日志文件设置为告警的输出文件
_CrtSetReportFile(_CRT_WARN, hLogFile);
3、 在程序执行结尾,我们输出内存泄露
}
//Dump从程序开始运行到该时刻点,已分配而未释放的内存
_CrtDumpMemoryLeaks();
CloseHandle(hLogFile);
我们按ctrl-F5执行程序,然后打开c:\ memleak.log看到内容如下:
Detected memory leaks!
Dumping objects ->
F:\2008\07\prj\memleak\memleak.cpp(22) : {61} normal block at 0x00386EB8, 1024 bytes long.
Data: CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{50} normal block at 0x00421CF0, 33 bytes long.
Data: 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD
{49} normal block at 0x00421D40, 40 bytes long.
Data: 14 7C 4C 10 16 00 00 00 00 00 00 00 00 00 00 00
{47} client block at 0x00421E50, subtype 0, 64 bytes long.
a CDynLinkLibrary object at $00421E50, 64 bytes long
Object dump complete.
在实际项目中,尤其是我们看一个长期运行的服务程序的内存泄露情况,可使用定时器定时获取从程序开始执行到当前时刻,有多少内存没有释放,这可能就是使用单纯F5调试内存泄露所不具有的手段。
学会如何防止并检查内存泄漏,是一个合格的c/c++程序员必须具备的能力。但是由于内存泄漏是程序运行并满足一定条件时才会发生,直接从代码中查出泄漏原因的难度较大。而且检查内存泄露的方法和工具很多,上面仅仅介绍了最简单也是最基础检查内存泄露的方法。希望感兴趣的读者一定要深入下去。
1-5 Windows 内存管理简述
最后,在内存分配上,不同操作系统具有一些特定的API函数,使用这些函数可使内存分配更高效、安全。例如在windows 系统上,具有一系列的函数负责内存分配、释放和管理。
用于内存管理的函数
CopyMemory 将一块内存从位置拷贝到另外一个位置,该函数使用频率非常高。
FillMemory 将指定内存块内容填充为指定数据
GetWriteWatch 查找已经被写入虚拟内存区域的页面地址
GlobalMemoryStatus 获得关于系统当前对于物理内存和虚拟的内存的使用信息。
GlobalMemoryStatusEx 获得关于系统当前对于物理内存和虚拟的内存的使用信息。
IsBadCodePtr 决定调用进程是否拥有对指定地址内存的读操作权。
IsBadReadPtr 检验调用进程是否拥有对指定内存范围的读操作权。
IsBadStringPtr 检验调用进程是否拥有对指定字符串所在地址区域的读操作权。
IsBadWritePtr 检验调用进程是否拥有对指定内存范围的写操作权。
MoveMemory 将一块内存从一个位置移动到另外的位置。
ResetWriteWatch 为某片虚拟内存区域重置写跟踪状态。
ZeroMemory 用零值填充某片内存块,该函数也是一个经常使用的函数。
AWE(Address Windowing Extensions) 函数
AllocateUserPhysicalPages 分配物理内存页面与进程的AWE区域建立或取消映射
FreeUserPhysicalPages 释放先前由AllocateUserPhysicalPages函数分配的物理内存页面。
MapUserPhysicalPages 映射在AWE区域内的指定地址分配的物理内存。
MapUserPhysicalPagesScatter映射在AWE区域内的指定地址分配的物理内存。
全局函数(global functions)
GlobalAlloc 从堆中分配指定字节数量的内存。
GlobalDiscard 丢弃指定的全局内存块。
GlobalFlags 返回关于指定全局内存对象的信息
GlobalFree 释放指定的全局内存对象。
GlobalHandle 返回指定全局内存块的指针的句柄。
GlobalLock 锁定一个全局内存对象并且返回指向该内存块第一个字节的指针。
GlobalReAlloc 改变指定全局内存对象的大小和属性。
GlobalSize 得到指定内存对象的当前大小。
GlobalUnlock 减少对一个内存对象的锁定数量。
本地(local)函数
LocalAlloc 从堆中分配指定数量的内存。
LocalDiscard 丢弃指定的本地内存对象。
LocalFlags 返回关于指定本地内存对象的信息。
LocalFree 释放指定的本地内存对象。
LocalHandle 得到指向指定本地内存对象的指针的句柄。
LocalLock 锁定本地内存对象并且返回指向该内存对象的第一个字节的指针。
LocalReAlloc 改变指定本地内存对象的大小或者属性。
LocalSize 返回指定本地内存对象的当前大小。
LocalUnlock 减少对某内存对象的锁定数量。
堆函数族
GetProcessHeap 获得调用进程的堆的一个句柄。
GetProcessHeaps 获得调用进程所有有效的堆的句柄。
HeapAlloc 从堆中分配一块内存。
HeapCompact 尝试压紧指定的堆。
HeapCreate 创建一个堆对象。
HeapDestroy 销毁指定的堆对象。
HeapFree 释放一块从堆中分配的内存。
HeapLock 尝试获得与指定堆关联的锁定。
HeapQueryInformation 获得关于指定堆的资料。
HeapReAlloc 从堆中重新分配一块内存。
HeapSetInformation 为指定的堆设置堆信息。
HeapSize 获得一个在堆上的内存块的大小。
HeapUnlock 获得与指定堆相关联的一个锁定的所有者。
HeapValidate 尝试使指定的堆有效。
HeapWalk 枚举指定堆上的内存块。
虚拟内存函数
VirtualAlloc 保留或提交调用进程虚拟地址空间的某一区域的页面。
VirtualAllocEx 保留或提交调用进程虚拟地址空间的某一区域的页面。
VirtualFree 释放或取消提交调用进程虚拟地址空间的某一区域的页面。
VirtualFreeEx 释放或取消提交调用进程虚拟地址空间的某一区域的页面。
VirtualLock 锁定指定的进程虚拟地址空间的指定块到物理内存中。
VirtualProtect 改变调用进程虚拟地址空间已提交页面区域的访问限制级。
VirtualProtectEx 改变调用进程虚拟地址空间已提交页面区域的访问限制级。
VirtualQuery 提供关于调用进程虚拟地址空间页面区域的资料。
VirtualQueryEx 提供关于调用进程虚拟地址空间页面区域的资料。
VirtualUnlock 对某进程虚拟地址空间的某区域的页面解锁。
限于本章主旨,上面函数系列不再深入介绍,感兴趣的读者可以去深入了解。
1-6 总结和建议读者的练习
1、 使用 OLLYDBG 反汇编分析C语言内存管理。
2、 包装内存分配和释放的函数,以探测内存泄露。
发表评论
-
计算机控制系统
2012-01-09 09:41 971计算机控制系统 2011年1 ... -
检测技术
2012-01-09 09:41 614检测技术 2011年12月19日 1.什么是热电效应,分 ... -
数字图像处理复习大纲
2012-01-09 09:40 1189数字图像处理复习大纲 ... -
单片机系统中常用的滤波算法
2012-01-09 09:40 967单片机系统中常用的滤波算法 2011年02月11日 // ... -
十一种通用滤波算法
2012-01-09 09:40 746十一种通用滤波算法 20 ... -
vbs教程
2012-01-08 09:25 1053vbs教程 2011年02月19日 ... -
VBS应用
2012-01-08 09:24 1167VBS应用 2011年05月05日 原文地址 http: ... -
VBS整人代码 很多 测试把我给整安逸了
2012-01-08 09:24 1118VBS整人代码 很多 测试把我给整安逸了 2010年08月2 ... -
vbs脚本文件执行提权技术技巧
2012-01-08 09:24 1352vbs脚本文件执行提权技 ... -
VBS获取网页内容
2012-01-08 09:24 2873VBS获取网页内容 2011年0 ... -
【学习】【Effective C++】条款41: 区分继承和模板
2012-01-07 09:15 734【学习】【Effective C++】条款41: 区分继承和模 ... -
c++
2012-01-07 09:15 939c++ 2010年04月14日 ht ... -
第五题
2012-01-07 09:15 546第五题 2010年07月07日 课程设计题目、要求: ... -
推荐一本好书:Effictive C++
2012-01-07 09:15 590推荐一本好书:Effictive C++ 2009年08月1 ... -
《中国当代艺术“价值观” 》 高岭 (一)
2012-01-06 10:06 502《中国当代艺术“价值 ... -
北京师范大学2011年艺术类本科招生简章
2012-01-06 10:05 558北京师范大学2011年艺术类本科招生简章 2012年01月0 ... -
具象艺术
2012-01-06 10:05 876具象艺术 2011年12月14日 2011-06-2 ... -
成都理工大学广播影视学院2012年艺术类招生简章
2012-01-06 10:05 1845成都理工大学广播影视 ... -
汤池婚礼作品《七年》摄制艺术分析
2012-01-06 10:05 701汤池婚礼作品《七年》摄制艺术分析 2011年04月21日 ... -
菊花情
2012-01-05 13:27 531菊花情 2009年10月08日 秋风瑟瑟,凉气袭人。万 ...
相关推荐
C语言内存管理详解C语言内存管理详解C语言内存管理详解C语言内存管理详解C语言内存管理详解C语言内存管理详解C语言内存管理详解C语言内存管理详解
有关C语言内存管理方面的培训资料,有兴趣的可以看下!
(麻省理工免费课程)C语言内存管理和C++面向对象编程,学习的好资料
C语言内存管理 C语言内存管理 C语言内存管理
比较详细的C语言内存管理讲解
通过C语言,基本实现了内存管理过程中的创建、修改、删除等操作
1-1 C语言内存管理方式 1-2 C语言内存管理 1-3 C语言内存使用要点及常见错误 1-4 在Windows下如何跟踪内存泄露 1-5 Windows内存管理简述 1-6 总结和建议读者的练习
对C语言中如何实现内存管理的详尽文档,对提升基础技术有非常重要的作用。
4. C语言内存对齐,提高寻址效率 5. 内存分页机制,完成虚拟地址的映射 6. 分页机制究竟是如何实现的? 7. MMU部件以及对内存权限的控制 8. Linux下C语言程序的内存布局(内存模型) 9. Windows下C语言程序的内存...
c语言内存管理注意事项,非常经典。。需要的
文档包括堆和栈 内存管理 内存调试 内存使用规则
内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其主要的目的是如何高校、快捷的分配,并且在适当的时候释放和回收内存资源。故了解C语言中的内存是怎么分配的,就可以高效、快速的达到内存的合理...
详 细 剖 析 内 语 言 内 存 管 理 机 制
C语言内存管理.pdf
c语言操作系统课程设计内存管理 nt addCalculate(int sn,int sd)//地址换算,sn为逻辑地址,sd为段内地址 { int add; if(sn>s.len) { printf("段号%d大于段表长度%d,越界中断\n",sn,s.len); return 0; } ...
本课件首先介绍了C语言中的内存管理知识,包括堆栈概念、动态内存分配函数及内存泄漏等问题,并给出了示例代码。然后详细讲解了C语言中的结构体与联合的定义、访问及嵌套使用,每部分都配有代码实例说明。内容结构清晰,...
网上下载的 本来是网页版 转成了pdf 不是我的原版哦
C语言内存管理[收集].pdf