Delphi7 之 内存篇(三)

  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;                         {}
时间: 2024-11-03 23:55:38

Delphi7 之 内存篇(三)的相关文章

Delphi7 之 内存篇(四)

一个使用私有堆的例子: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end; var Form1: TForm1; impleme

Delphi7 之 内存篇(五)

本例在建立一个新的堆前后分别通过 GetProcessHeaps 函数获取了当前进程的堆句柄列表, 效果图: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Memo2: TMemo; Button1: TButton; p

Python基础篇(三)

元组是序列的一种,与列表的区别是,元组是不能修改的. 元组一般是用圆括号括起来进行定义,如下: >>> (1,2,3)[1:2]     (2,) 如果元组中只有一个元素,元组的表示有些奇怪,末尾需要加上一个逗号: >>> (1,2,3)[1:2]     (2,) >>> 3*(3)      9      >>> 3*(3,)      (3, 3, 3) tuple函数 tuple函数用于将任意类型的序列转换为元组: >&

【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇三:批量处理后的txt文件入库处理

篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblogs.com/baiboy/p/wpf1.html 篇三:批量处理后的txt文件入库处理:http://www.cnblogs.com/baiboy/p/wpf2.html 篇四:关于OneNote入库处理以及审核:http://www.cnblogs.com/baiboy/p/wpf3.html [

【SSRS】入门篇(三) -- 为报表定义数据集

原文:[SSRS]入门篇(三) -- 为报表定义数据集 通过前两篇文件 [SSRS]入门篇(一) -- 创建SSRS项目 和 [SSRS]入门篇(二) -- 建立数据源 后, 我们建立了一个SSRS项目,并取得数据源,那么接下来做的就是知道报表要显示什么数据了,这一步可以通过建立数据集来实现. 1.解决方案资源管理器 ->右键选择共享数据集 ->添加新数据集: 2.在共享数据集属性窗口输入数据集名称:AdventureWorksDataset:数据源选择之前建立的:AdventureWorks

backbonejs中的模型篇(三)

一:在模型中使用嵌套属性 Backbone的扩展插件 Backbone-Nested下载并添加引用 1:定义一个新的模型对象,使用Backbone.NestedModel作为其基类对象 var _model = Backbone.NestedModel.extend({...})                    var testObj = new _model() 2: 使用'.'操作符来获取或设置嵌套属性值 testObj.set({ 'name.title': 'Mr.', 'name

Java提高篇(三二)-----List总结

前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点可以对List接口有了比较深的了解了.只有通过归纳总结的知识才是你的知识.所以下面LZ就List接口做一个总结.推荐阅读: java提高篇(二一)-----ArrayList java提高篇(二二)-----LinkedList java提高篇(二九)-----Vector Java提高篇(三一)-----Stack 一.List接口概述 List接口,成为

8.17_Linux之bash shell脚本编程入门篇(三)之循环以及函数function的使用

bash shell脚本编程入门篇(三)之循环 什么是循环执行? 将某代码段重复运行多次 重复运行多少次: 循环次数事先已知 循环次数事先未知 有进入条件和退出条件 相关命令:for, while, until,selet, for命令的使用 作用: 依次将列表中的元素赋值给"变量名"; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束 命令格式: for 变量名 in 列表; do 循环体(正常执行的执行命令) 语句1 语句2 语句3 ... done 列表生成方式: (

Android 终端性能测试——内存篇

前言 做Android QQ性能测试时,内存测试中遇到不少困惑,"各种"内存术语,到底什么意思,怎么获取,这里总结一下. 进行的内存测试主要有两个方面,一,OOM的发现和定位,二,同历史版本或竞品的对比测试.关于oom可以用MAT进行分析,具体分析方法参见susanwu在km上的文章<如何使用Memory_Analyzer分析内存泄漏>.下面主要总结一下Android性能测试中常用的方法及解释 一:running services"查看service进程内存 从A