【问题标题】:How to allocate class using HeapCreate/HeapAlloc?如何使用 HeapCreate/HeapAlloc 分配类?
【发布时间】:2015-11-05 20:53:10
【问题描述】:

如何在 Delphi 中使用 HeapCreate 和 HeapAlloc 分配类?下面的例子只会崩溃。

program Project2;

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows;

var
  SL: TStringList;
  Handle: THandle;
  P: ^TStringList;
begin
  Handle := HeapCreate(0, SizeOf(TStringList), SizeOf(TStringList));
  P := HeapAlloc(Handle, 0, SizeOf(TStringList));
  P.Add('some random string'); // crash

  HeapFree(Handle, 0, @P);
end.

【问题讨论】:

  • 你到底想在这里完成什么? SL 已经是一个指向TStringList 实例的指针,所以P 是一个指向一个TStringList 实例的指针
  • @KenWhite 我正在尝试使用 TStringList?
  • 这似乎充满了危险。听起来您真正想要的是 Delphi 的不同内存管理器。你试图做的事情是行不通的。考虑一下,即使您设法在新堆上创建 TStringList,其中的字符串仍将存在于共享堆上,因此您试图避免的任何争用都会失败(假设您就是这样)尝试做)。
  • 你通过写SL := TStringList.Create;来使用TStringList。您不需要HeapCreateHeapAlloc。 Delphi 自动将它们放在应用程序的堆上。看来您需要的是 Delphi 教程。
  • @KenWhite 不,我认为他正试图故意创建第二个堆以在多线程上下文中使用 - 以避免 MM 争用。当然,这完全不是这样做的方式,但我不认为 OP 的困惑在于如何以标准方式创建对象。

标签: delphi delphi-xe4


【解决方案1】:

Delphi 仅提供了有限的工具来从主内存管理器以外的任何地方分配对象。为此,您需要覆盖类的NewInstance 方法。这是构造函数调用来分配类的新实例的方法。销毁对应方是FreeInstance

覆盖这些方法并调用HeapAllocHeapFree。不过,SizeOf 并未给出要分配的字节数。 SizeOf 告诉您对象引用 的大小,它始终是SizeOf(Pointer)。您需要 instance 的大小,它由类的 InstanceSize 方法给出。

虽然您可以重写方法以使用您选择的内存分配策略,但您可能不会对结果感到满意,因为存在一些问题:

  1. NewInstance 方法不接受任何参数,因此您无法告诉类从哪个 堆分配。您可以有一个全局堆,或者每个类有一个堆,但您不能选择为每个实例单独使用哪个堆。

  2. NewInstance 方法由给定类的所有实例共享,因此无法在您的特殊堆上仅分配 一些 实例并从默认内存管理器分配其余实例.这主要是上一个问题的另一种表述方式。

  3. 您不能将其改装到现有的类中,因此您可以分配TMyStringList,但不能分配TStringList。 (嗯,你可以,但它需要修补每个类的 VMT,这通常不推荐。)


您尝试的代码存在一些问题。首先是您实际上从未分配过TStringList。您分配了一个 指针 到一个,正如我上面提到的,它是 SizeOf(Pointer) 字节。这没有足够的内存来保存 TStringList 实例。

您根本不需要^TStringList。您可以将分配的内存直接分配给TStringList 变量SL

SL := HealAlloc(Handle, 0, TStringList.InstanceSize);

注意我是如何调整大小的。

但这还不够,因为虽然它分配了一些内存,但它还没有构造对象。你需要调用构造函数。

SL.Create;

请注意,构造函数将分配更多内存来保存字符串列表,并分配更多内存来保存字符串内容。这些内存分配不会在堆上进行。他们会像往常一样转到默认内存管理器。

销毁对象将是另一个问题。您需要调用析构函数来释放字符串,但如果这样做,析构函数也会尝试释放对象的内存。它将使用默认内存管理器来释放它,但由于您没有使用默认内存管理器来分配TStringList 对象,内存管理器将抛出EInvalidPointer 异常。

Delphi 无法在不释放相关内存的情况下销毁对象。也就是说,你不能在不调用FreeInstance的情况下调用Destroy,但TStringList.FreeInstance会调用FreeMem,而不是HeapFree


我建议您从当前的任务中退后一步,重新审视您打算解决的问题,并考虑为某些对象建立一个单独的堆。可能会有更好的解决方案,而不需要花费太多精力来对抗您的工具的设计目标。

【讨论】:

  • 所以我需要一个自定义的 TStringList 和一个自定义的字符串?我需要一个每个线程堆。所以这是必不可少的。
  • 如果您需要每线程堆,则使用线程内存管理器。您不需要应用程序代码来指定每次分配使用哪个堆。您只需要内存管理器为每个线程使用不同的堆。当我建议你退后一步时,这就是我所说的。你问了一个 XY 问题
猜你喜欢
  • 2013-01-02
  • 1970-01-01
  • 2018-08-24
  • 2014-05-06
  • 2019-05-25
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 2012-01-03
相关资源
最近更新 更多