windows堆管理是建立在虚拟内存管理的基础之上的,每个进程都有独立的4GB的虚拟地址空间,其中有2GB的属于用户区,保存的是用户程序的数据和代码,而系统在装载程序时会将这部分内存划分为4个段从低地址到高地址依次为静态存储区,代码段,堆段和栈段,其中堆的生长方向是从低地址到高地址,而栈的生长方向是从高地址到低地址。

程序申请堆内存时,系统会在虚拟内存的基础上分配一段内存,然后记录下来这块的大小和首地址,并且在对应内存块的首尾位置各有相应的数据结构,所以在堆内存上如果发生缓冲区溢出的话,会造成程序崩溃,这部分没有硬件支持,所有管理算法都有开发者自己设计实现。

堆内存管理的函数主要有HeapCreate、HeapAlloc、HeapFree、HeapRealloc、HeapDestroy、HeapWalk、HeapLock、HeapUnLock。下面主要通过一些具体的操作来说明这些函数的用法。

堆内存的分配与释放

堆内存的分配主要用到函数HeapAlloc,下面是这个函数的原型:

LPVOID HeapAlloc(
HANDLE hHeap, //堆句柄,表示在哪个堆上分配内存
DWORD dwFlags, //分配的内存的相关标志
DWORD dwBytes //大小
);

堆句柄可以使用进程默认堆也可以使用用户自定义的堆,自定义堆使用函数HeapCreate,函数返回堆的句柄,使用GetProcessHeap可以获取系统默认堆,返回的也是一个堆句柄。分配内存的相关标志有这样几个值:

HEAP_NO_SERIALIZE:这个表示对堆内存不进行线程并发控制,由于系统默认会进行堆的并发控制,防止多个线程同时分配到了同一个堆内存,如果程序是单线程程序则可以添加这个选项,适当提高程序运行效率。

HEAP_ZERO_MEMORY:这个标志表示在分配内存的时候同时将这块内存清零。

HeapCreate函数的原型如下:

HANDLE HeapCreate(
DWORD flOptions, //堆的相关属性
DWORD dwInitialSize, //堆初始大小
DWORD dwMaximumSize //堆所占内存的最大值
);

flOptions的取值如下:

HEAP_NO_SERIALIZE:取消并发控制

HEAP_SHARED_READONLY:其他进程可以以只读属性访问这个堆

dwInitialSize, dwMaximumSize这两个值如果都是0,那么堆内存的初始大小由系统分配,并且堆没有上限,会根据具体的需求而增长。下面是使用的例子:

    //在系统默认堆中分配内存
srand((unsigned int)time(NULL));
HANDLE hHeap = GetProcessHeap();
int nCount = 1000;
float *pfArray = (float *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, nCount * sizeof(float));
for (int i = 0; i < nCount; i++)
{
pfArray[i] = 1.0f * rand();
} HeapFree(hHeap, HEAP_NO_SERIALIZE, pfArray); //在自定义堆中分配内存
hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0);
pfArray = (float *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, nCount * sizeof(float));
for (int i = 0; i < nCount; i++)
{
pfArray[i] = 1.0f * rand();
} HeapFree(hHeap, HEAP_NO_SERIALIZE, pfArray);
HeapDestroy(hHeap);

遍历进程中所有堆的信息:

便利堆的信息主要用到函数HeapWalk,该函数的原型如下:

BOOL WINAPI HeapWalk(
__in HANDLE hHeap,//堆的句柄
__in_out LPPROCESS_HEAP_ENTRY lpEntry//返回堆内存的相关信息
);

下面是PROCESS_HEAP_ENTRY的原型:

typedef struct _PROCESS_HEAP_ENTRY {
PVOID lpData;
DWORD cbData;
BYTE cbOverhead;
BYTE iRegionIndex;
WORD wFlags;
union {
struct {
HANDLE hMem;
DWORD dwReserved[3];
} Block;
struct {
DWORD dwCommittedSize;
DWORD dwUnCommittedSize;
LPVOID lpFirstBlock;
LPVOID lpLastBlock;
} Region;
};
} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY;

这个结构中的公用体具体使用哪个与wFlags相关,下面是这些值得具体含义:

wFlags 堆入口含义 lpData cbData cbOverhead块前堆数据结构大小 iRegionIndex Block Region
PROCESS_HEAP_ENTRY_BUSY 被分配的内存块 首地址 内存块大小 内存块前堆数据结构 所在区域索引 无意义 无意义
PROCESS_HEAP_ENTRY_DDESHARE DDE共享内存块 首地址 内存块大小 内存块前堆数据结构 所在区域索引 无意义 无意义
PROCESS_HEAP_ENTRY_MOVEABLE 可移动的内存块(兼容GlobalAllocLocalAlloc) 首地址(可移动内存句柄的首地址) 内存块大小 内存块前堆数据结构 所在区域索引 与PROCESS_HEAP_ENTRY_BUSY标志一同指定可移动内存句柄值 无意义
PROCESS_HEAP_REGION 已提交的堆虚拟内存区域 区域开始地址 区域大小 区域前堆数据结构 区域索引 无意义 虚拟内存区域详细信息
PROCESS_HEAP_UNCOMMITTED_RANGE 未提交的堆虚拟内存区域 区域开始地址 区域大小 区域前堆数据结构 区域索引 无意义 无意义

下面是时遍历堆内存的例子:

    PHANDLE pHeaps = NULL;
//当传入的参数为0和NULL时,函数返回进程中堆的个数
int nCount = GetProcessHeaps(0, NULL);
pHeaps = new HANDLE[nCount];
//获取进程所有堆句柄
GetProcessHeaps(nCount, pHeaps);
PROCESS_HEAP_ENTRY phe = {0};
for (int i = 0; i < nCount; i++)
{
cout << "Heap handle: 0x" << pHeaps[i] << '\n';
//在读取堆中的相关信息时需要将堆内存锁定,防止程序向堆中写入数据
HeapLock(pHeaps[i]);
HeapWalk(pHeaps[i], &phe);
//输出堆信息
cout << "\tSize: " << phe.cbData << " - Overhead: "
<< static_cast<DWORD>(phe.cbOverhead) << '\n';
cout << "\tBlock is a";
if(phe.wFlags & PROCESS_HEAP_REGION)
{
cout << " VMem region:\n";
cout << "\tCommitted size: " << phe.Region.dwCommittedSize << '\n';
cout << "\tUncomitted size: " << phe.Region.dwUnCommittedSize << '\n';
cout << "\tFirst block: 0x" << phe.Region.lpFirstBlock << '\n';
cout << "\tLast block: 0x" << phe.Region.lpLastBlock << '\n';
}
else
{
if(phe.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE)
{
cout << "n uncommitted range\n";
}
else if(phe.wFlags & PROCESS_HEAP_ENTRY_BUSY)
{
cout << "n Allocated range: Region index - "
<< static_cast<unsigned>(phe.iRegionIndex) << '\n';
if(phe.wFlags & PROCESS_HEAP_ENTRY_MOVEABLE)
{
cout << "\tMovable: Handle is 0x" << phe.Block.hMem << '\n';
}
else if(phe.wFlags & PROCESS_HEAP_ENTRY_DDESHARE)
{
cout << "\tDDE Sharable\n";
}
}
else cout << " block, no other flags specified\n";
}
cout << std::endl;
HeapUnlock(pHeaps[i]);
ZeroMemory(&phe, sizeof(PROCESS_HEAP_ENTRY));
}
delete[] pHeaps;

另外堆还有其他操作,比如使用HeapSize获取分配的内存大小,使用HeapValidate可以校验一个对内存的完整性,从而提早发现”野指针”等等。

windows 堆管理的更多相关文章

  1. Windows编程中的堆管理(过于底层,一般不用关心)

    摘要: 本文主要对Windows内存管理中的堆管理技术进行讨论,并简要介绍了堆的创建.内存块的分配与再分配.堆的撤销以及new和delete操作符的使用等内容. 关键词: 堆:堆管理 1 引言 在大多 ...

  2. windows虚拟内存管理

    内存管理是操作系统非常重要的部分,处理器每一次的升级都会给内存管理方式带来巨大的变化,向早期的8086cpu的分段式管理,到后来的80x86 系列的32位cpu推出的保护模式和段页式管理.在应用程序中 ...

  3. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  4. C++内存管理4-Windows编程中的堆管理(转)

    1 引言 在大多数Windows应用程序设计中,都几乎不可避免的要对内存进行操作和管理.在进行大尺寸内存的动态分配时尤其显的重要.本文即主要对内存管理中的堆管理技术进行论述. 堆(Heap)实际是位于 ...

  5. 实用算法系列之RT-Thread链表堆管理器

    [导读] 前文描述了栈的基本概念,本文来聊聊堆是怎么会事儿.RT-Thread 在社区广受欢迎,阅读了其内核代码,实现了堆的管理,代码设计很清晰,可读性很好.故一方面了解RT-Thread内核实现,一 ...

  6. Windwos堆管理体系以及溢出利用

    <0day安全>学习笔记,主要讨论WIndows2000~WIndowsSP1平台的堆管理策略. 0X01 堆与栈的区别 栈空间是在程序设计时已经规定好怎么使用,使用多少内存空间.典型的栈 ...

  7. 调试使用windows堆程序遇到的问题

    今天测试我的api hook demo,中间有个单向链表,我对他进行遍历的时候,通过判断链表当前元素是否为NULL(即0)来进行循环控制,在cmd下正常运行,输出的是:,struct addr is ...

  8. 无法从命令行或调试器启动服务,必须首先安装Windows服务(使用installutil.exe),然后用ServerExplorer、Windows服务器管理工具或NET START命令启动它

    无法从命令行或调试器启动服务,必须首先安装Windows服务(使用installutil.exe),然后用ServerExplorer.Windows服务器管理工具或NET START命令启动它 1. ...

  9. Windows上管理远程Linux VPS/服务器文件工具 - winscp

    Linux上经常会经常需要编辑文件,特别是Linux VPS/服务器安装好系统之后配置环境会需要修改很多的配置文件等,对于常用Linux的基本上都能够熟练使用vi或者nano等SSH下面的文件编辑工具 ...

随机推荐

  1. keeplived日志位置指定

    作为一个运维DBA,除了关心数据库的关键指数.还得往架构和底层基础知识多靠拢. 2010年刚工作的时候,那会Cacti监控比較流行吧.可恶的是SNMP会把默认日志写到系统日志文件中面,导致排错时非常受 ...

  2. 星云測试- Android应用深度体检专业平台

    星云測试-给你的Android应用做个深度体检   星云測试- Android应用深度体检专业平台 星云在线云測试(简称星云測试www.teststars.cc)是全球第一个公布并商用的数字化精准软件 ...

  3. css怎样让背景充满整个屏幕

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. ssm学习(四)--完整的增删改查demo

    上一篇文章简要介绍了将sping mvc加入整个框架,算是完成了ssm的集成.本节继续前面的内容,结合spring mvc做一个简单的增删改查demo. 1.首先,重写一下GeckoList.jsp页 ...

  5. 大数据学习(7)Hadoop高可用

    HDFS高可用 通过主从切换实现单NameNode高可用.通过Federation:水平扩展来联合多NameNode个: NameNode高可用 把edits日志从原来的nameNode中分离出来,存 ...

  6. 分布式:ACID, CAP, BASE

    本文主要讲述分布式系统开发的一些相关理论基础. 一.ACID ACID是一系列对系统中数据进行访问与更新的操作所组成的一个程序执行的逻辑单元,狭义上的事务特指数据库事务. 1.Atomic原子性 事务 ...

  7. utf8、unicode与gbk

    中国人民通过对 ASCII 编码(对于英文字母8位)的中文扩充改造,产生了 GB2312 编码,可以表示6000多个常用汉字. 汉字实在是太多了,包括繁体和各种字符,于是产生了 GBK (汉字:两个字 ...

  8. Hibernate--使用xml配置映射关系

    写在前面: 配置实体类与数据库的映射关系,有两种方式: 1.使用*.hbm.xml  2.使用@注解 二:xml的配置方式: eg:员工的xml配置文件: <?xml version=" ...

  9. Akka Cluster简介与基本环境搭建

      akka集群是高容错.去中心化.不存在单点故障以及不存在单点瓶颈的集群.它使用gossip协议通信以及具备故障自动检测功能. Gossip收敛   集群中每一个节点被其他节点监督(默认的最大数量为 ...

  10. wait/notify 实现多线程交叉备份

    一.任务 创建20个线程,其中10个线程是将数据备份到 A 数据库中,另外10 个线程将数据备份到 B 数据库中,并且备份 A 数据库和 备份 B 数据库的是交叉运行的. 二.实现 1.实现备份 A ...