Delphi 如何找到指针引用的内存大小?

Delphi 如何找到指针引用的内存大小?,delphi,memory-management,pointers,Delphi,Memory Management,Pointers,GetMem允许您分配任意大小的缓冲区。在某些地方,内存管理器会保留大小信息,因为当您将指针传递到FreeMem时,不需要告诉它缓冲区有多大 该信息仅供内部使用,还是有任何方法可以检索指针指向的缓冲区大小 该信息仅供内部使用,还是有任何方法可以检索指针指向的缓冲区大小 这两个“备选方案”相互矛盾吗 它仅供内部使用。在分配的存储元信息的区域之前有一些信息。这意味着,每次分配一块内存时,都会分配一块更大的内存,并且第一个字节用于元信息。返回的指针指向此元信息后面的块 我可以想象,内存管理器的另一个版

GetMem允许您分配任意大小的缓冲区。在某些地方,内存管理器会保留大小信息,因为当您将指针传递到FreeMem时,不需要告诉它缓冲区有多大

该信息仅供内部使用,还是有任何方法可以检索指针指向的缓冲区大小

该信息仅供内部使用,还是有任何方法可以检索指针指向的缓冲区大小

这两个“备选方案”相互矛盾吗


它仅供内部使用。

在分配的存储元信息的区域之前有一些信息。这意味着,每次分配一块内存时,都会分配一块更大的内存,并且第一个字节用于元信息。返回的指针指向此元信息后面的块


我可以想象,内存管理器的另一个版本改变了格式,所以不要指望这一点。

这些信息仅供内部使用


请注意,内存管理器不需要将大小存储为返回内存的一部分,许多内存管理器会将其存储在一个内部表中,并使用该表中作为查找键给出的块开始的内存地址。

它是供内部使用的,因为它取决于使用的内存管理器。顺便说一句,这就是为什么需要从同一内存管理器使用GetMem/FreeMem对;没有标准的方法可以知道如何保留内存。
在Delphi中,如果您查看FastMM4,您可以看到内存是按小、中或大的块分配的:
小块在固定大小块的池中分配(块大小在块类型的池级别定义)

中间块也在池中分配,但大小可变

  {Medium block layout:
   Offset: -8 = Previous Block Size (only if the previous block is free)
   Offset: -4 = This block size and flags
   Offset: 0 = User data / Previous Free Block (if this block is free)
   Offset: 4 = Next Free Block (if this block is free)
   Offset: BlockSize - 8 = Size of this block (if this block is free)
   Offset: BlockSize - 4 = Size of the next block and flags

  {Get the block header}
  LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^;
  {Get the medium block size}
  LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask;
大块以所需大小单独分配

  TLargeBlockHeader = packed record
    {Points to the previous and next large blocks. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousLargeBlockHeader: PLargeBlockHeader;
    NextLargeBlockHeader: PLargeBlockHeader;
    {The user allocated size of the Large block}
    UserAllocatedSize: Cardinal;
    {The size of this block plus the flags}
    BlockSizeAndFlags: Cardinal;
  end;

似乎由GetMem()返回的指针所引用的块的大小必须可以从某处获得,因为FreeMem()不需要您标识要释放的内存大小-系统必须能够确定,那么为什么应用程序开发人员不能

但是,正如其他人所说,所涉及的内存管理的确切细节并不是由系统本身定义的。。。。Delphi一直有一个可替换的内存管理器体系结构,为兼容内存管理器定义的“接口”不要求它们为任意指针提供此信息

默认的内存管理器将以任何适合的方式维护必要的信息,但是其他一些内存管理器几乎肯定会使用一种完全不同的(如果表面上相似的话)机制,因此,即使您基于对某个内存管理器的深入了解来破解一个解决方案,如果您更改内存管理器(或者,如果您更改了它,例如,您可能默认使用的系统定义内存管理器发生了更改,例如,在Delphi 2005和2006之间发生的更改),则您的解决方案几乎肯定会中断

一般来说,对于RTL/内存管理器来说,这并不是一个不合理的假设,即应用程序应该已经知道GetMem()allocated指针所指的内存块有多大,因为应用程序首先需要它!)

如果应用程序没有分配指针,那么应用程序的内存管理器绝对无法知道它引用的块有多大。例如,它可能是指向某个较大块中间的指针-只有指针的源可能知道它与它引用的内存的关系

但是,如果您的应用程序确实需要维护有关其自身指针的此类信息,那么它当然可以通过一个简单的单例类或函数库轻松设计实现这一点的方法,通过该类或函数库可以路由GetMem()/FreeMem()请求,为每个当前分配的指针维护相关请求大小的记录。这样的机制当然可以根据需要轻松地公开这些信息,完全可靠且独立于正在使用的内存管理器


如果需要“准确”的记录,这可能是唯一的选择,因为给定的内存管理器实现可能会为给定大小的数据分配比实际请求更大的内存块。我不知道是否有内存管理器真的这样做了,但为了效率起见,理论上它可以这样做。

相关:我的意思是,这些信息对开发人员隐藏是因为它们仅供内部使用,还是有办法获取它们?我的意思是,即使这些信息是供内部使用,你还是有办法的。也许不是。真正的说法是,这些信息仅供内部使用。其他的一切都不重要。我想答案取决于前缀的大小。似乎没有统一的方法来加载blocksize来实现这一点,这是如何工作的?现在分配中有大小吗?AnsiString有长度前缀,但这是因为RTL在调用
GetMem
之前向其添加了额外的4个字节。(再加上引用计数4,从Delphi 2009开始,代码页2,元素大小2。)当您延长字符串时,RTL调用
ReallocMem
,这允许内存管理器选择是就地扩大分配还是分配新块(使用新地址)。永远都是这样;解析不使用内存管理器的内部簿记。
  TLargeBlockHeader = packed record
    {Points to the previous and next large blocks. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousLargeBlockHeader: PLargeBlockHeader;
    NextLargeBlockHeader: PLargeBlockHeader;
    {The user allocated size of the Large block}
    UserAllocatedSize: Cardinal;
    {The size of this block plus the flags}
    BlockSizeAndFlags: Cardinal;
  end;