频道分类

Delphi内存对齐

作者:admin 来源: 日期:2019/12/1 22:44:32 人气: 标签:

 
本文环境Delphi XE 10.2  

我们知道在Delphi中,全局变量存储在应用程序数据段中,其生命周期直至整个应用进程终止;局部变量存储在应用程序栈中,其生命周期直至当前声明的函数方法返回;以GetMem,New等方法动态申请的内存由堆分配,其生命周期直至以FreeMem或Dispose方法销毁;长字符串(Long string), 宽字符串(wide string), 动态数组(dynamic array), 变体(variant),和接口(interface)的存储内存也是堆分配的, 但他们的内存管理是自动的(编译器自动插入引用计数相关代码); 对象(TObject)的存储内存通过构造函数由堆分配的,通过析构函数销毁。

Delphi高版本自带的内存管理器实现了堆分配的内存地址对齐。在Win32平台上,内存管理器分配的内存块起始地址默认对齐于8字节,也可以通过System.SetMinimumBlockAlignment设置对齐类型为16字节来提高访问性能。在Win64平台上,内存管理器分配的内存块起始地址对齐于16字节。

编译器的基础对齐支持:
1,静态数据(全局变量和typed常量)
按其类型对齐参数对齐以优化访问。

2,栈数据(局部变量)
编译器不保证对齐.

3,堆数据(GetMem或New分配内存,长字符串, 宽字符串, 动态数组, 变体, 接口,对象等)
在Win32上,对齐于8/16字节;在Win64上对齐于16字节。通常会有一个或多个存储堆分配内存地址的变量,用来访问堆数据。该变量本身可以位于静态数据(全局变量),栈数据(局部变量), 堆数据(如类成员变量)。

类型定义中的对齐成员:
对于数组类型,其对齐参数与单个数组元素的对齐参数相同,这样可以保证在处理数组时每一项都边界对齐。

可以通过编译指令"{$Align N}"或"{$A N}"来控制record和class内字段成员的最大对齐大小N,字段成员地址不对齐于“N与字段类型对齐参数中两者较小者”时,该成员前面将产生填充。N的默认值是8,可能取值有:1,2,4,8,16。定义结构体时如果指定 N=1,则所有成员都不需要填充,此时与使用"packed record"关键字效果一样。

可以在每一个成员前面增加注解“[Align(M)]”来显式为每个成员指定一个对齐值M。M可能取值有:1,2,4,8,16。

可以在类型定义前面增加注解“[Align(L)]”来显式为定义的类型指定对齐参数L;结构体还可以在定义的结尾处使用“Align L”方式显示指定类型对齐参数L。L可能取值有:1,2,4,8,16。

1)每个成员按照被声明的顺序在内存中顺序存储,结构体类型的第一个成员的地址和整个类型的地址相同。因此对结构体类型第一个成员显式指定M值是无效的;

2)如果某个成员显式指定了对齐值M,则该成员地址将对齐于M(无论自身类型对齐参数是什么,也不无论当前N值是什么);

3)否则,该成员按其类型的对齐参数(简单类型通常是这个类型的大小)和编译指令指定的对齐参数N中较小的值对齐;

4)新定义类型的对齐参数等于“L,N,最大成员类型对齐参数”三者中的最大值;

5)新类型的整体长度是新类型对齐参数的整数倍;如果不够类型末尾将被填充。

type
  PSLIST_ENTRY = ^SLIST_ENTRY;
  SLIST_ENTRY = record
    Next: PSLIST_ENTRY;
  end Align N;
 
  [Align(N)] 
  SLIST_ENTRY = record
    Next: PSLIST_ENTRY;
  end;
在Win32平台,指针大小是4字节,所以SLIST_ENTRY默认大小是4字节,其对齐参数也是4字节。如果我们按上面的定义方式令N=8,编译器将在Next之后填充4字节使SLIST_ENTRY大小变为8字节,其对齐参数也变为8字节。同理N还可设为16,但设置小于4的值将无效果。


代码对齐
Code align makes sure that procedure entry points are aligned on specified boundaries.  {$CODEALIGN 16}
————————————————

原文链接:https://blog.csdn.net/tht2009/article/details/80533519