已阅读5页,还剩2页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
delphi 堆和栈转2.1 栈栈是由操作系统在创建线程的时候,系统自动创建,栈是由顶像下分配的,DELPHI中默认的栈大小是1M,这个可以通过Project-Options-Linker-Max Stack size来改变其大小。栈是线程执行代码的地方,操作系统根据系统调度算法来加载执行的代码,另外栈还存放函数的参数值,局部变量。栈的存取是按4字节偏移,不会根据需要动态增长,因此超出范围会报栈溢出。2.2 堆我们把在栈之外的分配内存都叫在堆上分配内存,堆是由程序员分配释放。在DELPHI中是用GetMem.inc中的代码来管理堆的,堆中包含许多大小不确定的块。初始状态下,堆仅有一个块,即堆本身。经过一段时间地取用和回收以后,堆中将可能只剩下一些“切割”后残余的“碎片”,且这些碎片可能已经无法再合并。此时,如果一个新的请求大于任何一个碎片,那么就必须再申请一个新的、大的块放在堆中。堆的使用永远是一个“拆东墙补西墙”的过程。堆的大小是2G,在扩展内存模式下能达到3G。注意它与数据结构中的堆是两回事,它的分配方式类似于链表,访问“堆”的内容的时候需要先找到这个“堆”,然后再遍历链表,因此“堆”访问会比“栈”慢。2.3 哪些在栈中2.3.1 获取栈的首尾地址获取通常情况下的栈地址在写汇编的时候,我们知道esp存放栈顶指针,ebp存放栈底指针procedure GetStackAddress(var AStackTop, AStackBottom: Cardinal);begin asm mov eax, esp; /栈顶,eax接收第一个参数 mov edx, ebp; /栈底,edx接收第二个参数 end;end;获取异常发生时的栈地址在Windows下,FS:4存放发生异常时的栈顶指针。procedure GetStackAddress(var AStackTop, AStackBottom: Cardinal);begin asm mov ecx, FS:4; /FS:4放置发生异常时的栈信息 sub ecx, 3; mov eax, eax; /栈顶,eax接收第一个参数 mov edx, ebp; /栈低,edx接收第二个参数 end;end;知道了栈的首尾地址之后,我们就可以取出变量地址,然后和栈的地址比较,如果超出栈的范围,则表示变量在堆中。2.3.2基本数据类型:函数体中-栈;类中-堆基本数据类型(Integer、Cardinal、Shortint、Smallint、Longint、Int64、Byte、Word、LongWord、Char)在函数体内分配是在栈中的,如果在类中分配则是在堆中的。另外Int64也是在栈中分配的,它具体的分配是偏移8字节。我们写下如下测试代码:procedure TestInt64;var Value: Int64; StackTop, StackBottom: Cardinal;begin Value := 10; GetStackAddress(StackTop, StackBottom); ShowMessage(Format(StackTop: %s, StackBottom: %s; Int64 Address: %s, IntToHex(StackTop, 8), IntToHex(StackBottom, 8), IntToHex(Integer(Value), 8);end;我电脑测试显示的信息为StackTop: 0012F5E0, StackBottom: 0012F628; Int64 Address: 0012F620,从上面信息我们可以看出栈底偏8字节就是Value的地址。2.3.3 指针类型:指针-栈,指针的内容-堆指针在函数体内分配,指针的地址是在栈中的,指针的内容是在堆中的。指针如果在类中分配则,指针地址和指针内容都是在堆中的。我们写下如下测试代码:procedure TestPointer;var APoint: Pointer; StackTop, StackBottom: Cardinal;begin GetMem(APoint, 1000); GetStackAddress(StackTop, StackBottom); ShowMessage(Format(StackTop: %s, StackBottom: %s; Pointer Address: %s; Pointer Content Address: %s, IntToHex(StackTop, 8), IntToHex(StackBottom, 8), IntToHex(Integer(APoint), 8), IntToHex(Integer(APoint), 8);end;我的电脑测试显示的信息为StackTop: 0012F568, StackBottom: 0012F5B8; Pointer Address: 0012F5B4; Pointer Content Address: 00A3FD10,从上面的信息我们可以栈底偏4字节就是指针的地址。2.3.4 固定数组:函数体中-栈;类中-堆固定数组在函数体内分配是在栈中的,如果在类中分配则是在堆中的。因此不能函数体内分配超过1M大小的固定数组,否则会造成栈溢出。我们写下如下测试代码:type TFixArray = array0.9 of Integer;procedure TestFixArray;var FixArray: TFixArray; StackTop, StackBottom: Cardinal;begin FixArray0 := 10; GetStackAddress(StackTop, StackBottom); ShowMessage(Format(StackTop: %s, StackBottom: %s; Int64 Address: %s, IntToHex(StackTop, 8), IntToHex(StackBottom, 8), IntToHex(Integer(FixArray0), 8);end;我的电脑测试显示的信息为StackTop: 0012F550, StackBottom: 0012F5B8; Fix Array Address: 0012F588,从上面的信息我们可以看出固定数组是在栈中的,动态数组类似指针,只是动态数组的指针在栈中,动态数组的内容是在堆中的。另外我们从汇编代码也可以看出相同的信息,FixArray0 := 10对应的汇编代码是mov ebp-$30,$0000000a,ebp指向栈底,因此我们可以看出动态数组的内存是在栈中的。2.3.5 结构体:函数体中-栈;类中-堆结构体在函数体内分配是在栈中的,在类中分配则是在堆中的,如果结构体内含有string等指针类型,则指针的地址在栈内,指针的内容是在堆中的。在函数体内分配超过1M大小的结构体也会造成栈溢出。我们写下如下测试代码:type TRecord = record Value: string; Len: Integer; end;procedure TestRecord;var PntRecord: TRecord; StackTop, StackBottom: Cardinal;begin PntRecord.Value := Test; GetStackAddress(StackTop, StackBottom); ShowMessage(Format(StackTop: %s, StackBottom: %s; Record Address: %s; Record Pointer Address: %s, IntToHex(StackTop, 8), IntToHex(StackBottom, 8), IntToHex(Integer(PntRecord), 8), IntToHex(Integer(PChar(PntRecord.Value), 8);end;我的电脑测试显示的信息为StackTop: 0012F564, StackBottom: 0012F5B8; Record Address: 0012F5B0; Record Pointer Address: 0045F4B4,从上面的信息我们可以看出结构体是在栈中的,结构体指针和指针一样。2.4 哪些在堆中用内存申请函数申请的内存都是在堆中的,如用New、GetMem、StrAlloc、AllocMem、SysGetMem,哪些自管理类型string、动态数组的内容都是在堆中的,下面我们给出结论,测试代码大家可以仿照上面的判断变量是否在栈中的代码编写。2.4.1 指针指向的内容是在堆中2.4.2 动态数组的内容是在堆中2.4.3 String、ShortString、WideString的内容是在堆中2.4.4 变体Variant、OleVariant的内容是在堆中变体类型是一个结构体,它的定义是:TVarData = packed record case Integer of 0: (VType: TVarType; case Integer of 0: (Reserved1: Word; case Integer of 0: (Reserved2, Reserved3: Word; case Integer of varSmallInt: (VSmallInt: SmallInt); varInteger: (VInteger: Integer); varSingle: (VSingle: Single); varDouble: (VDouble: Double); varCurrency: (VCurrency: Currency); varDate: (VDate: TDateTime); varOleStr: (VOleStr: PWideChar); varDispatch: (VDispatch: Pointer); varError: (VError: HRESULT); varBoolean: (VBoolean: WordBool); varUnknown: (VUnknown: Pointer); varShortInt: (VShortInt: ShortInt); varByte: (VByte: Byte); varWord: (VWord: Word); varLongWord: (VLongWord: LongWord); varInt64: (VInt64: Int64); varString: (VString: Pointer); varAny: (VAny: Pointer); varArray: (VArray: PVarArray); varByRef: (VPointer: Pointer); ); 1: (VLongs: array0.2 of LongInt); ); 2: (VWords: array 0.6 of Word); 3: (VBytes: array 0.13 of Byte); ); 1: (RawData: array 0.3 of LongInt); end;从定义中我们可以看出varOleStr、varString、varArray、varByRef都是在堆中的。2.5 全局变量在堆中全局变量的指针地址和指针内容都是在栈中的,我们把他归类到堆中。2.6 栈和堆比较2.6.1 栈和堆的管理方式比较栈:由操作系统自动分配,而且在栈上分配内存是由编译器自动完成的,栈不需要编译器管理,操作系统自动实现申请释放;堆:由操作系统提供接口,各个编译器实现管理方式,由外部程序申请释放,如果外部程序在程序结束时没有释放,由操作系统强行释放,在DELPHI中是用GetMem.inc来实现内存管理;2.6.2 栈和堆的初始化比较栈:分配的内存不会初始化,是一个垃圾值;堆:分配的内存不会初始化,是一个垃圾值,但是DELPHI默认初始化类变量和全局变量;2.6.3 栈和堆的申请方式比较栈:由系统自动分配,如在函数申明一个局部变量i: Integer;编译器会自动在栈中分配内存;堆:由程序自己管理,需要程序员自己申请,并指明大小;2.6.4 堆和栈的效率比较栈:在栈上分配空间是直接用add指令,对esp进行移位,例如add esp,-$44,可以在一个指令周期内完成;堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的FreeMem语句才能正确的释放本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 在堆中分配内存的时候会用HeapLock和HeapUnlock加锁,因此在多线程中分配内存是线性的,效率低下;2.6.5 栈和堆的大小限制比较栈:在Windows下栈默认大小是1M, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 堆:在Windows下默认堆大小是2GB,堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 地面维修服务合同范本
- 女儿签合同买车签协议
- 家政承包物业合同范本
- 外贸销售劳动合同范本
- 外墙做旧服务合同范本
- 土地托管保险合同范本
- 大型比赛活动合同协议
- 器材拆装搬运合同范本
- 园林地建厂房合同范本
- 多款客车租赁合同范本
- 2025北京银行笔试行测判断推理真题
- 航海船舶风险评估报告
- 2025年中级注册安全工程师《安全生产管理》考前三十页纸
- 九年级仁爱英语上册-期中试题卷(原卷板+解析版+听力音频)
- 幼儿园三重一大集体决策管理方案
- 单位建食堂方案模板(3篇)
- 水利工程灾情评估者2025洪水灾害预测与防治方案
- 甘肃省兰州市第五中学2024-2025学年七年级上学期期中考试数学试卷(含答案)
- GB/T 20805-2025饲料中酸性洗涤木质素(ADL)的测定
- 2025年法学专业基础知识考试试题及答案
- 折叠技术基础知识培训课件
评论
0/150
提交评论