zoukankan      html  css  js  c++  java
  • 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;                         {}
    

     

  • 相关阅读:
    HDU 5744
    HDU 5815
    POJ 1269
    HDU 5742
    HDU 4609
    fzu 1150 Farmer Bill's Problem
    fzu 1002 HangOver
    fzu 1001 Duplicate Pair
    fzu 1150 Farmer Bill's Problem
    fzu 1182 Argus 优先队列
  • 原文地址:https://www.cnblogs.com/delphi2014/p/4034462.html
Copyright © 2011-2022 走看看