当前位置: 首页 > news >正文

内存管理[3]

为什么80%的码农都做不了架构师?>>>   hot3.png

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;                         {}

 
 
 
 
 

 

 

  
举例放下篇吧.

转载于:https://my.oschina.net/hermer/blog/320225

相关文章:

  • Graphics 单元下的公用函数目录
  • 入口文件开始,分析Vue源码实现
  • hive可以drop所有表的bug fix
  • 标准化 归一化
  • MongoDB命令
  • 【转】nGrinder 简易使用教程
  • (备忘)Java Map 遍历
  • Linux vmstat命令实战详解
  • LintCode 31. partitionArray 数组划分
  • ASP.NET-FineUI开发实践-6(二)
  • 十大经典排序算法(动图演示)(转载)
  • 责任链模式的两种实现
  • eclipse 导入自定义jar包
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • Appach 服务器如让IP绑定多个域名
  • conda常用的命令
  • css属性的继承、初识值、计算值、当前值、应用值
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • JAVA_NIO系列——Channel和Buffer详解
  • Making An Indicator With Pure CSS
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • python学习笔记 - ThreadLocal
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • vue的全局变量和全局拦截请求器
  • Vue官网教程学习过程中值得记录的一些事情
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 分布式事物理论与实践
  • 记一次删除Git记录中的大文件的过程
  • 漂亮刷新控件-iOS
  • 使用agvtool更改app version/build
  • 微信公众号开发小记——5.python微信红包
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 我的面试准备过程--容器(更新中)
  • 赢得Docker挑战最佳实践
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​MySQL主从复制一致性检测
  • ​人工智能书单(数学基础篇)
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #WEB前端(HTML属性)
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (篇九)MySQL常用内置函数
  • (七)理解angular中的module和injector,即依赖注入
  • (五)Python 垃圾回收机制
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转) RFS+AutoItLibrary测试web对话框
  • (转)http协议
  • ****Linux下Mysql的安装和配置
  • .bashrc在哪里,alias妙用
  • .NET : 在VS2008中计算代码度量值
  • .NET HttpWebRequest、WebClient、HttpClient