delphi 内存管理与内存泄漏

基本数据类型/结构体/对象/数组 的 创建/拷贝/释放

  1. 测试demo
  • unit1

    unit Unit1;
    
    interface
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, StrUtils;
    
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        test3: TButton;
        test1: TButton;
        test2: TButton;
        test4: TButton;
        test5: TButton;
        test6: TButton;
        Edit1: TEdit;
        btn1: TButton;
        procedure test1Click(Sender: TObject);
        procedure test2Click(Sender: TObject);
        procedure test3Click(Sender: TObject);
        procedure test4Click(Sender: TObject);
        procedure test5Click(Sender: TObject);
        procedure test6Click(Sender: TObject);
        procedure Memo1DblClick(Sender: TObject);
        procedure btn1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        procedure writeLog(s: string);
      end;
    
    
    var
      Form1: TForm1;
    
    implementation
    uses Unit2;
    
    {$R *.dfm}
    
     procedure TForm1.writeLog(s: string);
    begin
      Memo1.Lines.Add(s);
    end;
    
    procedure TForm1.test1Click(Sender: TObject);
    var
      Util: TUtil;
    begin
      //字符串与字符串数组
      Util := TStrArrUtil.create;
      writeLog(Util.Show);
      writeLog(Util.toString);
      FreeAndNil(Util);
    end;
    
    procedure TForm1.test2Click(Sender: TObject);
    var
      Util: TUtil;
    begin
    //基本类型数组
      Util := TByteArrUtil.create;
      writeLog(Util.Show);
      writeLog(Util.toString);
      FreeAndNil(Util);
    end;
    
    procedure TForm1.test3Click(Sender: TObject);
    var
      Util: TUtil;
      p: Pointer;
    begin
    //对象数组
      Util := TObjArrUtil.create;
      p := @TObjArrUtil(Util).ObjArr[0];
      writeLog(IntToStr(dword(p)));
    
      writeLog(Util.Show);
      writeLog(Util.toString);
      FreeAndNil(Util);
    
      writeLog(TNewObject(p^).objName); //必须重写destroy,否则内存泄漏
    end;
    
    procedure TForm1.test4Click(Sender: TObject);
    var
      Util: TUtil;
    begin
    //结构体数组 基本类型 引用类型
      Util := TRecArrUtil.create;
      writeLog(Util.Show);
      writeLog(Util.toString);
    //  FreeAndNil(Util);
      Util.Free;
    end;
    
    procedure TForm1.test5Click(Sender: TObject);
    var
      objsrc, objDest: TNewObject;
    begin
      writeLog('TNewObject.create');
    //对象拷贝assign  浅复制与深复制
      objsrc := TNewObject.create('srcObj');
      writeLog('subObj.create');
      objsrc.subObj := TNewObject.create('subObj');
      writeLog(objsrc.objName);
      writeLog(objsrc.subObj.objName);
    
      writeLog('objsrc.deepClone');
      objDest := objsrc.deepClone; //深拷贝,生成新对象
      writeLog(objDest.objName);
      writeLog(objDest.subObj.objName);
    
      objDest.objName := 'Test';
      objDest.subObj.objName := 'subTest';
      writeLog('---------------- objsrc');
      writeLog(objsrc.objName);
      writeLog(objsrc.subObj.objName);
    
      writeLog('---------------- objDest');
      writeLog(objDest.objName);
      writeLog(objDest.subObj.objName);
    
      FreeAndNil(objsrc);  //必须重写destroy,否则内存泄漏
      FreeAndNil(objDest); //必须重写destroy,否则内存泄漏
    end;
    
    procedure TForm1.test6Click(Sender: TObject);
    var
      Util: TUtil;
      i, iMax: Integer;
      p: Pointer;
    begin
      //内存回收
      iMax := StrToIntDef(Trim(Edit1.Text), 10);
      for i:= 1 to iMax do
      begin
        Util := TobjRecArrUtil.create;
        p := @TobjRecArrUtil(Util).objRecArr[0];
        writeLog(IntToStr(dword(p)));
    
        writeLog(Util.Show + IntToStr(i));
        writeLog(Util.toString);
        FreeAndNil(Util); //必须重写destroy,否则内存泄漏
      end;
    
      writeLog(TobjRec(p^).objNew.objName);
    end;
    
    procedure TForm1.Memo1DblClick(Sender: TObject);
    begin
      Memo1.Lines.Clear;
    end;
    
    procedure TForm1.btn1Click(Sender: TObject);
    var
      //静态数组 结构体 定长,声明即自动分配内存,可以直接复制
      recArr, recArr2: TRecArr;
      byteArr, byteArr2: TByteAarr;
      StrArr, StrArr2:TStrArr;
      ObjArr, ObjArr2: TObjArr;
      //动态数组 不定长, 只是指向同一内容要手动分配内存循环单独复制或者Copy
      bArr, bArr2: array of Byte;
      sArr, sArr2: array of string;
      sTemp: String;
      i: Integer;
    begin
      for i:= Low(byteArr) to High(byteArr) do
      begin
        byteArr[i] := i;
      end;
      //复制
      byteArr2 := byteArr;
      //CopyMemory(@byteArr2, @byteArr, Length(byteArr));
      for i:= Low(byteArr2) to High(byteArr2) do
      begin
        byteArr2[i] := i + 10;
      end;
      writeLog(TByteArrUtil.Show(byteArr));
      writeLog(TByteArrUtil.Show(byteArr2));
    
    
      for i:= Low(recArr) to High(recArr) do
      begin
        recArr[i].b := i;
        recArr[i].Bool := (i mod 2) = 1;
        recArr[i].int := ord(inttostr(i)[1]);
      end;
      //复制
      recArr2 := recArr;
      //CopyMemory(@recArr2, @recArr, Length(byteArr));
      for i:= Low(recArr2) to High(recArr2) do
      begin
        recArr2[i].b := i + 10;
        recArr2[i].Bool := (i mod 2) = 1;
        recArr2[i].int := i+ 20;
      end;
      writeLog(TRecArrUtil.Show(recArr));
      writeLog(TRecArrUtil.Show(recArr2));
    
    
      for i:= Low(StrArr) to High(StrArr) do
      begin
        StrArr[i] := 'src' + IntToStr(i);
      end;
      //复制 数组内字符串还会复制
      //StrArr2 := StrArr;
      //CopyMemory(@StrArr2, @StrArr, Length(StrArr)); //内存错: 数组string无空间
      for i:= Low(StrArr2) to High(StrArr2) do
      begin
        StrArr2[i] := 'dest' + IntToStr(i);
      end;
      writeLog(TStrArrUtil.Show(StrArr));
      writeLog(TStrArrUtil.Show(StrArr2));
    
    
      //数组会自动回收,字符串就是字符数组,也会自动回收
      //setLength分批内存有个过程,循环中对一个数组反复setlength会oom
    
      //栈内存(局部变量、字符串、数组)等会自动回收
      //堆内存(对象、getmem)需要手动回收,否则会有内存泄漏
      //result不宜为array 或者 string,且不宜在循环中反复调用。因为内存分批与回收有过程
      //假如机器性能低、回收不及时容易oom
    
    
      //基本类型数组和字符串复制可以直接复制, 也可以循环单个复制
      //对象数组,只会复制数组内存,2个数组内指针地址相同,指向内容相同. 只能循环单个重新复制赋值
      for i:= Low(ObjArr) to High(ObjArr) do
      begin
        ObjArr[i] := TNewObject.create(TNewObject.ClassName + IntToStr(i));
        ObjArr[i].subObj := TNewObject.create('subObj' + IntToStr(i));
      end;
      //复制
      ObjArr2 := ObjArr;
      //CopyMemory(@StrArr2, @StrArr, Length(StrArr)); //内存错: 数组string无空间
      for i:= Low(ObjArr2) to High(ObjArr2) do
      begin
        ObjArr2[i].objName := TNewObject.ClassName + IntToStr(i + 10);
        ObjArr2[i].subObj.objName := 'subObj' + IntToStr(i + 10);
        //ObjArr2[i] := TNewObject.create(TNewObject.ClassName + IntToStr(i + 10));
        //ObjArr2[i].subObj := TNewObject.create('subObj' + IntToStr(i + 10));
      end;
      writeLog(TObjArrUtil.Show(ObjArr));
      writeLog(TObjArrUtil.Show(ObjArr2));
    
    
      //动态数组 只是引用,不会在复制时分配新内存
      SetLength(bArr, 10);
      for i:= Low(bArr) to High(bArr) do
      begin
        bArr[i] := i;
      end;
      //复制  不重新分配内存
      //bArr2 := bArr;
      bArr2 := Copy(bArr);
      for i:= Low(bArr2) to High(bArr2) do
      begin
        bArr2[i] := i + 10;
      end;
    
      sTemp := '';  
      for i:= Low(bArr) to High(bArr) do
      begin
        sTemp := sTemp + inttostr(bArr[i]) + ' ' + #13#10;
      end;
      writeLog(sTemp);
    
      sTemp := '';
      for i:= Low(bArr2) to High(bArr2) do
      begin
        sTemp := sTemp + inttostr(bArr2[i]) + ' ' + #13#10;
      end;
      writeLog(sTemp);
    
    
      SetLength(sArr, 10);
      for i:= Low(sArr) to High(sArr) do
      begin
        sArr[i] := 'src' + IntToStr(i);
      end;
      //sArr2 := sArr; //不重新分配内存
      sArr2 := Copy(sArr);
      for i:= Low(sArr2) to High(sArr2) do
      begin
        sArr2[i] := 'dest' + IntToStr(i);
      end;
    
      sTemp := '';
      for i:= Low(sArr) to High(sArr) do
      begin
        sTemp := sTemp + inttostr(i) + ': ' + sArr[i] + #13#10;
      end;
      writeLog(sTemp);
    
      sTemp := '';
      for i:= Low(sArr2) to High(sArr2) do
      begin
        sTemp := sTemp + inttostr(i) + ': ' + sArr2[i] + #13#10;
      end;
      writeLog(sTemp);
    end;
    
    
    end.
  • unit2

unit Unit2;

interface
  uses SysUtils;

const
  I_Low = 0;
  I_High = 9;
  MB = 1024 * 1024;
  MEM_SIZE = MB * 10;

type
  TNewObject = class;

  TBaseRec = record
    b: Byte;
    Bool: Boolean;
    int: Integer;
  end;
  TarrRec = array [I_Low .. I_High] of TBaseRec;

  TobjRec = record
    s: string;
    obj: TObject;
    objNew: TNewObject;
  end;

  TRecArr = array [I_Low .. I_High] of TBaseRec;
  TByteAarr = array[I_Low .. I_High] of Byte;
  TStrArr = array[I_Low .. I_High] of string;
  TObjArr = array[I_Low .. I_High] of TNewObject;
  TobjRecArr = array [I_Low .. I_High] of TobjRec;


  TNewObject = class
  public
    p: Pointer;
    objName: string;
    subObj: TNewObject; //free能释放干净么?有没有内存泄漏?
    
    function deepClone(): TNewObject;
    constructor create(sName: string = 'TNewObject');
    destructor destroy(); override;
  end;

  TUtil = class
  public
    procedure Init(); virtual;
    function toString(): string; virtual;
    function Show(): string; virtual;
    constructor create(); virtual;
  end;

  TRecArrUtil = class(TUtil)
  private
    fRecArr: TRecArr;
  public
    procedure Init(); override;
    function toString(): string; override;
    function Show(): string; overload; override; 
    class function Show(var RecArr: TRecArr): string; overload;
  end;

  TByteArrUtil = class(TUtil)
  private
    fByteAarr: TByteAarr;
  public
    procedure Init(); override;
    function toString(): string; override;
    function Show(): string; overload; override; 
    class function Show(var ByteAarr: TByteAarr): string; overload;
  end;

  TStrArrUtil = class(TUtil)
  private
    fStrArr: TStrArr;
  public
    procedure Init(); override;
    function toString(): string; override;
    function Show(): string; overload; override; 
    class function Show(var StrArr: TStrArr): string; overload;
  end;

  TObjArrUtil = class(TUtil)
  private
    fObjArr: TObjArr;
  public
    procedure Init(); override;
    function toString(): string; override;
    function Show(): string; overload; override;
    class function Show(var ObjArr: TObjArr): string; overload;
    destructor destroy(); override; //不加会内存泄漏
    property ObjArr: TObjArr read fObjArr;         
  end;

  TobjRecArrUtil = class(TUtil)
  private
    fobjRecArr: TobjRecArr;
  public
    procedure Init(); override;
    function toString(): string; override;
    function Show(): string; override;
    destructor destroy(); override; //不加会内存泄漏
    property objRecArr: TobjRecArr read fobjRecArr;
  end;

implementation

{ TNewObject }

constructor TNewObject.create(sName: string = 'TNewObject');
begin
//  GetMem(p, MEM_SIZE); // out of memory
  objName := sName;
end;

function TNewObject.deepClone: TNewObject;
begin
  Result := TNewObject.Create;
  Result.objName := Self.objName;
  if Assigned(subObj) then
    Result.subObj := subObj.deepClone;
end;

destructor TNewObject.destroy;
begin
//  FreeMem(p);
  if Assigned(subObj) then subObj.Free;
end;

{ TUtil }

constructor TUtil.create;
begin
  Init;
end;

procedure TUtil.Init;
begin

end;

function TUtil.Show: string;
begin
  Result := Self.ClassName + '.show' + #13#10;
end;

function TUtil.toString: string;
begin
  Result := Self.ClassName + '.toString' + #13#10;
end;

{ TRecArrUtil }

procedure TRecArrUtil.Init;
var
  i: Integer;
begin
  for i:= Low(fRecArr) to High(fRecArr) do
  begin
    fRecArr[i].b := i;
    fRecArr[i].Bool := (i mod 2) = 1;
    fRecArr[i].int := ord(inttostr(i)[1]);
  end;
end;

function TRecArrUtil.Show: string;
var
  i: Integer;
begin
  for i:= Low(fRecArr) to High(fRecArr) do
  begin
    Result := Result + inttostr(i) + ': ' + inttostr(fRecArr[i].b) + ' ' + BoolToStr(fRecArr[i].Bool) + ' ' + inttostr(fRecArr[i].int) + #13#10;
  end;
end;

class function TRecArrUtil.Show(var RecArr: TRecArr): string;
var
  i: Integer;
begin
  for i:= Low(RecArr) to High(RecArr) do
  begin
    Result := Result + inttostr(RecArr[i].b) + ' ' + booltostr(RecArr[i].Bool) + ' ' + inttostr(RecArr[i].int) + #13#10;
  end;
end;

function TRecArrUtil.toString: string;
begin
  Result := Show(fRecArr);
end;


{ TObjArrUtil }

destructor TObjArrUtil.destroy;
var
  i: Integer;
begin
  for i:= Low(fObjArr) to High(fObjArr) do
  begin
    fObjArr[i].Free;
  end;
  inherited;
end;


procedure TObjArrUtil.Init;
var
  i: Integer;
begin
  for i:= Low(fObjArr) to High(fObjArr) do
  begin
    fObjArr[i] := TNewObject.create(TNewObject.ClassName + IntToStr(i));
    fObjArr[i].subObj := TNewObject.create('subObj' + IntToStr(i));
  end;
end;

function TObjArrUtil.Show: string;
begin
  Show(fObjArr);
end;

class function TObjArrUtil.Show(var ObjArr: TObjArr): string;
var
  i: Integer;
begin
  for i:= Low(ObjArr) to High(ObjArr) do
  begin
    Result := Result + inttostr(i) + ': ' + ObjArr[i].objName + ' ';
    if Assigned(ObjArr[i].subObj) then
      Result := Result + ObjArr[i].subObj.objName;
    Result := Result + #13#10;
  end;
end;


function TObjArrUtil.toString: string;
var
  i: Integer;
begin
  for i:= Low(fObjArr) to High(fObjArr) do
  begin
    Result := Result + inttostr(i) + ': ' + fObjArr[i].objName + ' ';
    if Assigned(fObjArr[i].subObj) then
      Result := Result + fObjArr[i].subObj.objName + #13#10;
  end;
end;

{ TobjRecArrUtil }

destructor TobjRecArrUtil.destroy;
var
  i: Integer;
begin
  for i:= Low(fobjRecArr) to High(fobjRecArr) do
  begin
    if Assigned(fobjRecArr[i].objNew) then
      fobjRecArr[i].objNew.Free;

    if Assigned(fobjRecArr[i].obj) then
      fobjRecArr[i].obj.Free;
  end;
  inherited;
end;

procedure TobjRecArrUtil.Init;
var
  i: Integer;
begin
  for i:= Low(fobjRecArr) to High(fobjRecArr) do
  begin
    fobjRecArr[i].s := IntToStr(i);
    fobjRecArr[i].obj := TObject.Create;
    fobjRecArr[i].objNew := TNewObject.create(TNewObject.ClassName + IntToStr(i));
    fobjRecArr[i].objNew.subObj := TNewObject.create('subObj' + IntToStr(i));
  end;
end;

function TobjRecArrUtil.Show: string;
var
  i: Integer;
begin
  for i:= Low(fobjRecArr) to High(fobjRecArr) do
  begin
    Result := Result + inttostr(i) + ': ' + fobjRecArr[i].s + ' ' + fobjRecArr[i].obj.ClassName + ' ' + fobjRecArr[i].objNew.objName + ' ';
    if Assigned(fobjRecArr[i].objNew.subObj) then
      Result := Result + fobjRecArr[i].objNew.subObj.objName;
    Result := Result + #13#10;
  end;
end;

function TobjRecArrUtil.toString: string;
var
  i: Integer;
begin
  for i:= Low(fobjRecArr) to High(fobjRecArr) do
  begin
    Result := Result + inttostr(i) + ': ' + fobjRecArr[i].s + ' ' + fobjRecArr[i].obj.ClassName + ' ' + fobjRecArr[i].objNew.objName + ' ';
    if Assigned(fobjRecArr[i].objNew.subObj) then
      Result := Result + fobjRecArr[i].objNew.subObj.objName;
    Result := Result + #13#10;
  end;
end;


{ TByteArrUtil }

procedure TByteArrUtil.Init;
var
  i: Integer;
begin
  for i:= Low(fByteAarr) to High(fByteAarr) do
  begin
    fByteAarr[i] := i;
  end;
end;

function TByteArrUtil.Show: string;
begin
 Result := Show(fByteAarr);
end;

class function TByteArrUtil.Show(var ByteAarr: TByteAarr): string;
var
  i: Integer;
begin
  for i:= Low(ByteAarr) to High(ByteAarr) do
  begin
    Result := Result + inttostr(ByteAarr[i]) + ' ' + #13#10;
  end;
end;

function TByteArrUtil.toString: string;
var
  i: Integer;
begin
  for i:= Low(fByteAarr) to High(fByteAarr) do
  begin
    Result := Result + inttostr(i) + ': ' + inttostr(fByteAarr[i]) + ' ' ;
  end;
end;



{ TStrArrUtil }

procedure TStrArrUtil.Init;
var
  i: Integer;
begin
  for i:= Low(fStrArr) to High(fStrArr) do
  begin
    fStrArr[i] := 'str' + inttostr(i);
  end;
end;

function TStrArrUtil.Show: string;
begin
  Result := Show(fStrArr);
end;

class function TStrArrUtil.Show(var StrArr: TStrArr): string;
var
  i: Integer;
begin
  for i:= Low(StrArr) to High(StrArr) do
  begin
    Result := Result + inttostr(i) + ': ' + StrArr[i] + #13#10;
  end;
end;

function TStrArrUtil.toString: string;
var
  i: Integer;
begin
  for i:= Low(fStrArr) to High(fStrArr) do
  begin
    Result := Result + fStrArr[i] + ' ' + #13#10;
  end;
end;

end.
  • 运行结果

delphi 内存管理与内存泄漏_delphi