内存溢出使用带有泛型的对象列表

时间:2015-02-19 12:22:05

标签: delphi generics memory delphi-xe7

第1步: 使用代码编写应用程序:

unit Unit1;

interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Generics.Collections,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;

type
TObjChild = class;

TObjTest = class
private

    FName: string;  
    FChilds: TList<TObjChild>;

public
    property Name: string read FName write FName;
    property Childs: TList<TObjChild> read FChilds write FChilds;

    constructor Create;
    destructor Destroy; override;
end;

TObjChild = class
private

    FAdress: string;  
    FPostalCode: string;

public

    property Adress: string read FAdress write FAdress;
    property PostalCode: string read FPostalCode write FPostalCode;

end;

TForm1 = class(TForm)

    Button1: TButton;
    procedure Button1Click(Sender: TObject);

private
{ Private declarations }

public
{ Public declarations }

end;

var
Form1: TForm1;

implementation

{$R *.fmx}
{ TObjTeste }

constructor TObjTest.Create;
begin

    FChilds := TObjectList<TObjChild>.Create;

end;

destructor TObjTest.Destroy;
var

    i: integer;

begin

    for i := 0 to FChilds.count -1 do
    begin
        FChilds[I].Free;
    end;

    FreeAndNil(FChilds);
    inherited;

end;

procedure TForm1.Button1Click(Sender: TObject);
var

    I: Integer;
    ListObjs: TList<TObjTest>;
    lObjTeste: TObjTest;
    lObjChild: TObjChild;
    J: Integer;

begin

    ListObjs := TList<TObjTest>.Create;

    for I := 0 to 5000 do
    begin
        lObjTeste := TObjTest.Create;

        for J := 0 to 2000 do
        begin
            lObjChild := TObjChild.Create;
            lObjTeste.FChilds.Add(lObjChild)
        end;

        ListObjs.Add(lObjTeste);

    end;

    if MessageDlg('Delete objects?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbOK], 0) = idOK then
    begin
        for I := 0 To ListObjs.Count - 1
        begin
            ListObjs[I].Free;
        end;

        FreeAndNil(ListObjs);
    end;

end;

end.

步骤2:运行应用程序并按下按钮1

按下确定按钮后,应用程序的消息dlg不释放内存

步骤3:重复步骤有时应用程序返回低内存

1 个答案:

答案 0 :(得分:3)

问题在于:

constructor TObjTest.Create;
begin
  FChilds := TObjectList<TObjChild>.Create;
end;

destructor TObjTest.Destroy;
var
  i: integer;
begin
  for i := 0 to FChilds.count - 1 do
  begin
    FChilds[i].Free;
  end;
  FreeAndNil(FChilds);
  inherited;
end;

默认情况下,TObjectList<T>取得其成员的所有权。所以你不需要,也不应该释放析构函数中的成员。

所以这里:

for i := 0 to FChilds.count - 1 do
begin
  FChilds[i].Free;
end;
你释放会员。但接着在这里:

FreeAndNil(FChilds);

对象列表还释放成员。谁已经被释放了。双重免费会导致运行时错误。

修复方法是删除显式释放对象列表成员并依赖列表来完成工作:

destructor TObjTest.Destroy;
begin
  FChilds.Free;
  inherited;
end;

其成员的所有权是TObjectList<T>存在的唯一原因。这是它提供的唯一功能,超出TList<T>提供的功能。在此处阅读:http://docwiki.embarcadero.com/Libraries/en/System.Generics.Collections.TObjectList

最后,儿童的复数是儿童。