文档章节

内存管理[3]

涂孟超
 涂孟超
发布于 2014/09/26 15:36
字数 1238
阅读 6
收藏 0
VirtualAlloc 分配的内存是以 4K 为最小单位、连续的内存地址(但映射到真实的内存时它不一定是连续的), 前面说了, 它不适合分配小内存(譬如只有几个字节的变量); 局部的变量在 "栈" 中有程序自动管理, 那么那些全局的小变量怎么办呢? 这就要用到 "堆".

这样看来, VirtualAlloc 分配的内存既不是 "栈" 也不是 "堆"; VirtualAlloc 分配的内存地址是连续的, "堆" 中内容一般是不连续的, 所以管理 "堆" 比较麻烦, 它是通过双线链表的结构方式管理的; 程序可以拥有若干个 "堆", 每一个 "堆" 都会有一个句柄, 访问 "堆" 中的内容时先要找到这个 "堆", 然后再遍历链表, 这可能就是 "堆" 比 "栈" 慢的根本原因.

在 "堆" 中分配内存(HeapAlloc)前先要建立 "堆"(HeapCreate), 就像程序有默认的 "栈" 一样, 每一个程序都有一个默认建立的 "堆"(可以用 GetProcessHeap 获取这个 "默认堆" 的句柄), 我们在 Delphi 中用到 "堆" 时, 使用的就是这个 "默认堆". 如果让程序更灵活地拥有多个 "堆", 必须要用到 API 函数.

建立 "堆" 时会同时提交真实内存的, 这在申请大内存时会很慢, 所以默认堆也只有 1M, 但 "默认堆" 并没有限制大小, 它会根据需要动态增长.

有了 "默认堆" 还有必要申请其他的 "堆" 吗? 这只有在多线程中才能体现出来, 和 "栈" 不一样, 程序会给每个线程分配一个 "栈区"; 而 "默认堆" 是进程中的所有线程公用的, 当一个线程使用 "默认堆" 时, 另一个需要使用 "堆" 的线程就要先挂起等待, 也就是它们不能同时使用; 只有通过 API 函数重新建立的私有堆才是互不干涉、最有效率的.

先了解一下 "堆" 相关的函数.
//建立堆; 注意建立时指定的尺寸也是按页大小(PageSize)对齐的, 譬如指定 15k, 实际会分配 16K.
HeapCreate(
  flOptions: DWORD;     {堆属性选项, 见下表}
  dwInitialSize: DWORD; {初始尺寸, 单位是字节; 该大小会被直接提交到实际的内存}
  dwMaximumSize: DWORD  {最大尺寸, 如果不限定最大值就设为 0}
): THandle;             {返回堆句柄; 失败返回 0, 但如果参数 flOptions 允许了异常, 失败会返回异常标识}

//flOptions 参数可选值:
HEAP_NO_SERIALIZE        = 1; {非互斥, 此标记可允许多个线程同时访问此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {当建立堆出错时, 此标记可激发一个异常并返回异常标识}
HEAP_ZERO_MEMORY         = 8; {把分配的内存初始化为 0}

//flOptions 参数指定有 HEAP_GENERATE_EXCEPTIONS 时, 可能返回的异常:
STATUS_ACCESS_VIOLATION = DWORD($C0000005); {参数错误}
STATUS_NO_MEMORY        = DWORD($C0000017); {内存不足}

 
 
 
 
 

 

 

  
//销毁堆 HeapDestroy( hHeap: THandle {堆句柄} ): BOOL; {}
//从堆中申请内存 HeapAlloc( hHeap: THandle; {堆句柄} dwFlags: DWORD; {内存属性选项, 见下表} dwBytes: DWORD {申请内存的大小, 单位是字节} ): Pointer; {返回内存指针; 失败返回 0 或异常, 情况和建立堆是一样} //dwFlags 参数可选值: HEAP_NO_SERIALIZE = 1; {非互斥, 此标记可允许多个线程同时访问此堆} HEAP_GENERATE_EXCEPTIONS = 4; {当建立堆出错时, 此标记可激发一个异常并返回异常标识} HEAP_ZERO_MEMORY = 8; {把分配的内存初始化为 0} {能看出这和堆的属性选项是一样的; 如果 dwFlags 参数设为 0, 将使用堆的属性; 如果重新指定将覆盖堆的属性} {另外: 如果堆是默认堆, 也就是堆句柄来自 GetProcessHeap, dwFlags 参数会被忽略}
//改变堆内存的大小, 也就是重新分配 HeapReAlloc( hHeap: THandle; {句柄} dwFlags: DWORD; {内存属性选项; 该参数比 HeapAlloc 多出一个选项, 见下表} lpMem: Pointer; {原内存指针} dwBytes: DWORD {新的尺寸} ): Pointer; {同 HeapAlloc} //dwFlags 参数可选值: HEAP_NO_SERIALIZE = 1; {非互斥, 此标记可允许多个线程同时访问此堆} HEAP_GENERATE_EXCEPTIONS = 4; {当建立堆出错时, 此标记可激发一个异常并返回异常标识} HEAP_ZERO_MEMORY = 8; {把分配的内存初始化为 0} HEAP_REALLOC_IN_PLACE_ONLY = 16; {此标记不允许改变原来的内存位置}
//获取堆中某块内存的大小 HeapSize( hHeap: THandle; {堆句柄} dwFlags: DWORD; {内存属性; 可选值是 0 或 HEAP_NO_SERIALIZE, 后者可确保同步访问} lpMem: Pointer {内存指针} ): DWORD; {成功返回字节为单位的大小; 失败返回 $FFFFFFFF}
//释放堆中指定的内存块 HeapFree( hHeap: THandle; {堆句柄} dwFlags: DWORD; {内存属性; 可选值是 0 或 HEAP_NO_SERIALIZE} lpMem: Pointer {内存指针} ): BOOL; {}
//验证堆 HeapValidate( hHeap: THandle; {} dwFlags: DWORD; {} lpMem: Pointer {} ): BOOL; {}
//整理堆 HeapCompact( hHeap: THandle; {} dwFlags: DWORD {} ): UINT; {}
//锁定堆 HeapLock( hHeap: THandle {} ): BOOL; {}
//锁定后的解锁 HeapUnlock( hHeap: THandle {} ): BOOL; {}
//列举堆中的内存块 HeapWalk( hHeap: THandle; {} var lpEntry: TProcessHeapEntry {} ): BOOL; {}
举例放下篇吧.

本文转载自:http://www.cnblogs.com/del/archive/2008/05/08/1188715.html

共有 人打赏支持
涂孟超
粉丝 12
博文 2011
码字总数 14107
作品 0
深圳
程序员
私信 提问
海思方案中地址空间的分配与使用

1、DDR内存管理说明 (1)所有DDR内存中,一部分由操作系统管理,称为OS内存;另一部分由MMZ模块管理,供媒体业务单独使用,称为MMZ内存。 (2)OS内存起始地址为0x80000000,内存大小可通过...

天糊土
09/22
0
0
Memcached与Redis(三)

3. Memcached和Redis关键技术对比 作为内存数据缓冲系统,Memcached和Redis均具有很高的性能,但是两者在关键实现技术上具有很大差异,这种差异决定了两者具有不同的特点和不同的适用条件。下...

liujing07
06/26
0
0
tcmalloc jemalloc glibc内存分配管理模块性能测试对比

tcmalloc是谷歌提供的内存分配管理模块 jemalloc是FreeBSD提供的内存分配管理模块 glibc是Linux提供的内存分配管理模块 并发16个线程,分配压测3次,每次压15分钟,可以看到谷歌的tcmalloc内...

技术小甜
2017/11/08
0
0
OC内存管理常见的面试题

#####面试:说一说对内存管理的理解?(原理) - 手动内存管理(MRC):1.在创建一个对象的时候系统会自动创建这个对象的引用计数并赋值为1;2.当引用计数为0的时候,对象会去调用dealloc方法来...

鹿微微鹿
2016/03/25
275
0
iOS中的内存管理(上)

下列行为都会增加一个app的内存占用: 1、创建一个OC对象; 2、定义一个变量; 3、调用一个函数或者方法。 如果app占用内存过大,系统可能会强制关闭app,造成闪退现象,影响用户体验。如何让...

傲风凌寒
2014/05/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

/etc/profile和/etc/environment的区别

/etc/profile 文件 当一个用户登录Linux系统或使用 su 命令切换到另一个用户时,设置用户环境第一个读取的文件就是 /etc/profile ,此文件为系统全局变量配置文件,且仅仅在第一次登录系统时...

calmsnow
10分钟前
0
0
rabbitMQ日常管理(转)

一、网页登录方法 http://127.0.0.1:15672/ 用户名和密码默认为guest/guest 用java代码去连接rabbitmq用的端口是5672 二、rabbitMQ基本概念 RabbitMQ是一个开源的AMQP实现,服务器端用Erlan...

__HuWei
17分钟前
1
0
gitlab cicd

https://docs.gitlab.com/ee/ci/docker/using_docker_build.html

kut
17分钟前
0
0
使用Prometheus+Grafana监控

一、介绍Prometheus Prometheus(普罗米修斯)是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。随着发展,越来越多公司和组织接受采用Prometheus,社会也十分活...

xtof
18分钟前
0
0
EOS RPC API官方文档中文版【1.5版】

EOS RPC API是应用访问EOS区块链上智能合约的必备开发接口,根据所实现插件的不同,EOS RPC API被归入不同的分组: CHAIN:由chain_api_plugin实现,主要提供区块链数据的访问功能 HISTORY:...

汇智网教程
20分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部