Delphi 离开堆栈时自动释放内存

Delphi 离开堆栈时自动释放内存,delphi,memory,memory-management,Delphi,Memory,Memory Management,我想编写一些程序,帮助我在指向动态分配的地址空间的指针离开堆栈时自动释放内存。 例如: procedure FillMemory (var mypointer); begin // CopyMemory, Move, etc... with data end; procedure MyProcedure; var MyPointer : Pointer; begin MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RE

我想编写一些程序,帮助我在指向动态分配的地址空间的指针离开堆栈时自动释放内存。 例如:

procedure FillMemory (var mypointer);
begin
  // CopyMemory, Move, etc... with data
end;

procedure MyProcedure;
var
  MyPointer : Pointer;
begin
  MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  FillMemory (MyPointer);
  VirtualFree(MyPointer, 0, MEM_RELEASE); // I would like to avoid this...
end;
我可以使用字符串,但我也希望避免使用它们(我不确定堆栈中的字符串是否得到释放…)
有什么想法吗?

托管类型会对其引用进行计数,当计数降至零时,它们将被最终确定。如果您有一个局部变量,那么当它超出范围时,其引用计数将降至零

因此,您可以使用接口创建所引用的
TInterfacedObject
的后代。大概是这样的:

type
  TLifetimeWatcher = class(TInterfacedObject)
  private
    FDestroyProc: TProc;
  public
    constructor Create(const DestroyProc: TProc);
    destructor Destroy; override;
  end;

constructor TLifetimeWatcher.Create(const DestroyProc: TProc);
begin
  inherited Create;
  FDestroyProc := DestroyProc;
end;

destructor TLifetimeWatcher.Destroy;
begin
  if Assigned(FDestroyProc) then
    FDestroyProc();
  inherited;
end;
procedure MyProcedure;
var
  MyPointer: Pointer;
  LifetimeWatcher: IInterface;
begin
  MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, 
    PAGE_EXECUTE_READWRITE);
  LifetimeWatcher := TLifetimeWatcher.Create(
    procedure
    begin
      VirtualFree(MyPointer, 0, MEM_RELEASE);
    end;
  )
  FillMemory(MyPointer);
end;
function VirtualAllocAutoRelease(Size: SIZE_T; Protect: DWORD;
  out LifetimeCookie: IInterface): Pointer;
var
  Ptr: Pointer;
begin
  Ptr := VirtualAlloc(nil, Size, MEM_COMMIT or MEM_RESERVE, Protect);
  Win32Check(Ptr<>nil);
  LifetimeCookie := TLifetimeWatcher.Create(
    procedure
    begin
      VirtualFree(Ptr, 0, MEM_RELEASE);
    end
  );
  Result := Ptr;
end;
procedure MyProcedure;
var
  MyPointer: Pointer;
  LifetimeWatcher: IInterface;
begin
  MyPointer := VirtualAllocAutoRelease(1024, PAGE_EXECUTE_READWRITE, 
    LifetimeWatcher);
  FillMemory(MyPointer);
end;
然后您可以这样使用它:

type
  TLifetimeWatcher = class(TInterfacedObject)
  private
    FDestroyProc: TProc;
  public
    constructor Create(const DestroyProc: TProc);
    destructor Destroy; override;
  end;

constructor TLifetimeWatcher.Create(const DestroyProc: TProc);
begin
  inherited Create;
  FDestroyProc := DestroyProc;
end;

destructor TLifetimeWatcher.Destroy;
begin
  if Assigned(FDestroyProc) then
    FDestroyProc();
  inherited;
end;
procedure MyProcedure;
var
  MyPointer: Pointer;
  LifetimeWatcher: IInterface;
begin
  MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, 
    PAGE_EXECUTE_READWRITE);
  LifetimeWatcher := TLifetimeWatcher.Create(
    procedure
    begin
      VirtualFree(MyPointer, 0, MEM_RELEASE);
    end;
  )
  FillMemory(MyPointer);
end;
function VirtualAllocAutoRelease(Size: SIZE_T; Protect: DWORD;
  out LifetimeCookie: IInterface): Pointer;
var
  Ptr: Pointer;
begin
  Ptr := VirtualAlloc(nil, Size, MEM_COMMIT or MEM_RESERVE, Protect);
  Win32Check(Ptr<>nil);
  LifetimeCookie := TLifetimeWatcher.Create(
    procedure
    begin
      VirtualFree(Ptr, 0, MEM_RELEASE);
    end
  );
  Result := Ptr;
end;
procedure MyProcedure;
var
  MyPointer: Pointer;
  LifetimeWatcher: IInterface;
begin
  MyPointer := VirtualAllocAutoRelease(1024, PAGE_EXECUTE_READWRITE, 
    LifetimeWatcher);
  FillMemory(MyPointer);
end;
LifetimeWatcher
离开作用域时,实现对象将被销毁,并执行传递给
TLifetimeWatcher.Create
的过程

将这个想法专门用于您的用例是很容易的。这将使呼叫站点的代码更加简洁

看起来是这样的:

type
  TLifetimeWatcher = class(TInterfacedObject)
  private
    FDestroyProc: TProc;
  public
    constructor Create(const DestroyProc: TProc);
    destructor Destroy; override;
  end;

constructor TLifetimeWatcher.Create(const DestroyProc: TProc);
begin
  inherited Create;
  FDestroyProc := DestroyProc;
end;

destructor TLifetimeWatcher.Destroy;
begin
  if Assigned(FDestroyProc) then
    FDestroyProc();
  inherited;
end;
procedure MyProcedure;
var
  MyPointer: Pointer;
  LifetimeWatcher: IInterface;
begin
  MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, 
    PAGE_EXECUTE_READWRITE);
  LifetimeWatcher := TLifetimeWatcher.Create(
    procedure
    begin
      VirtualFree(MyPointer, 0, MEM_RELEASE);
    end;
  )
  FillMemory(MyPointer);
end;
function VirtualAllocAutoRelease(Size: SIZE_T; Protect: DWORD;
  out LifetimeCookie: IInterface): Pointer;
var
  Ptr: Pointer;
begin
  Ptr := VirtualAlloc(nil, Size, MEM_COMMIT or MEM_RESERVE, Protect);
  Win32Check(Ptr<>nil);
  LifetimeCookie := TLifetimeWatcher.Create(
    procedure
    begin
      VirtualFree(Ptr, 0, MEM_RELEASE);
    end
  );
  Result := Ptr;
end;
procedure MyProcedure;
var
  MyPointer: Pointer;
  LifetimeWatcher: IInterface;
begin
  MyPointer := VirtualAllocAutoRelease(1024, PAGE_EXECUTE_READWRITE, 
    LifetimeWatcher);
  FillMemory(MyPointer);
end;

为了扩大我与Arioch的评论和讨论

如果您只需要一个原始内存块,请使用字节的动态数组。编译器将生成代码,以便在方法结束时释放此内存:

type
  TBytes = array of Byte; // omit for newer Delphi versions

procedure FillMemory(var Bytes: TBytes);
begin
  { passing in here will increase the reference count to 2 }
  // CopyMemory, Move, etc... with data
end; // then drop back to 1

procedure MyProcedure;
var
  Buffer : TBytes;
begin
  SetLength(Buffer, 1024);  // buffer has reference count of 1
  FillMemory (Buffer);
end;   // reference count will drop to 0, and Delphi will free memory here

希望这一切都有意义。现在是午夜,所以我感觉不是最清醒的…

字符串不在堆栈中。它们是指向堆的引用计数指针。通过引用计数机制,一旦为零,内存就被释放。这由字符串和动态数组的编译器管理。您的问题的一个可能解决方案是使用字节的动态
数组
,在以后的(2009+?)Delphi版本中,它被预定义为TBytes()@BenjaminWeiss-我相信是编译器创建了必要的代码。如果您正在尝试执行我认为您正在尝试执行的操作,则需要修改编译器。但是,如果不添加某种类型的引用计数,这可能是一个相当危险的“特性”——例如,编译器如何知道,如果在方法结束时,指针仍然指向分配的内存,或者指针指向的是它根本不应该释放的内存?是否要为Delphi创建垃圾收集器?接口引用,如
TInterfacedObject
IUnknown
等。RAII模式,是吗?看看+1,这就是OP想要的:自动分配原始内存,当代码超出范围时自动释放。谈论引用计数是个好主意-但是使用
var
它不会增加引用计数,AFAIK!:)如果不使用
var
const
而只使用原始参数,则参考计数会增加。请更正。我还用
TBytes=Byte数组
纠正了一个小的类型定义错误。@ArnaudBouchez是吗?这个问题用
PAGE\u EXECUTE\u READWRITE
@DavidHeffernan调用
VirtualAlloc
,我没有看到页面\u EXECUTE\u READWRITE,你是对的。但我怀疑OP不需要它,除非他知道如何动态生成asm存根,鉴于他的问题,我对此表示怀疑。他写道,他“绕过了RTL”,这是毫无意义的——在这种情况下,基于类的方法也不会起作用@也许是阿诺·布切斯。我只是从表面上回答这个问题。另外,Benjamin以前问过很多
VirtualAlloc
问题。问题中没有提到绕过RTL。我不知道这是从哪里来的。在任何情况下,您不需要RTL来生成类,而不需要实现接口的类,而不需要接口的自动引用计数。@ DavidHeffernan,所以您不考虑ToTobe和TIniterfacedObject的RTL部分的实现??OP想要绕过整个GETMEM进程(见他的评论)。因此,对他来说,使用类是不可能的所有这些听起来有点困惑。。。但是你的答案完全正确(即使我怀疑他使用的是Delphi处理委托的版本)。@ArnaudBouchez问题中没有任何东西表明类是不可行的。他最近的一个问题是标记为XE4,但无论如何,我们为什么要被限制在Delphi7上@ArnaudBouchez虽然我看不出这有多大的实际意义,但是您为什么不能在不使用堆管理器的情况下创建一个单一用途的接口实现类呢?是的,您必须重写.NewInstance等等,但这是可能的,不是吗?再次强调,我不谈论从中获得的实际利益,只谈论你的“不可能”这个答案是一个很好的选择,甚至考虑了
PAGE\u EXECUTE\u READWRITE
+1@Arioch这听起来像是弗兰肯斯坦在给我编码。至于这个问题,OP想要绕过整个getmem过程,因此,除非覆盖
NewInstance
,否则使用类并不能解决他的问题。。。。但是,我们如何才能找到一个好的办法来解决一个有偏见的问题呢?