Quản lý bộ nhớ
Những vấn đề quản lý bộ nhớ cần được quan tâm
Những vấn đề quản lý bộ nhớ cần được quan tâm
Không như một số ngôn ngữ khác như Java hay C# sử dụng bộ thu gom gác GC (Garbage Collection) để tự động giải phóng bộ nhớ cho các đối tượng không sử dụng, thì với delphi, bạn phải thực hiện nó thủ công.
Nếu như đối tượng được tạo ra mà không được giải phóng, hiện tượng rò rỉ bộ nhớ (memory leak) sẽ xảy ra. Trong trường hợp rò rỉ bộ nhớ nghiêm trọng, ứng dụng của bạn sẽ chiếm hết toàn bộ dung lượng Ram trống, và sẽ bị dừng.
Tuy nhiên, quản lý bộ nhớ thủ công không khó như bạn nghĩ.
Rò rỉ bộ nhớ không chỉ dành cho object. Gọi hàm mở file AssignFile mà không đóng (CloseFile), hoặc nạp dll bằng LoadLibrary mà không unload nó (FreeLibrary) cũng sinh ra memory leak.
Đây là cấu trúc để sử dụng một object an toàn trong delphi.
SimpleObject := TSimpleClass.Create;
try
...
finally
SimpleObject.Free;
end;
Các thao tác với SimpleObject được đặt ở sau try (phần ...). Sau khi thực hiện xong các lệnh trong try, khối finally sẽ được thực thi và Free đối tượng. Nếu trong phần ... xảy ra lỗi, các lệnh còn lại trong try sẽ bị bỏ qua và nhảy ngay đến finally, thực hiện giải phóng đối tượng.
Do đó, trong trường hợp nào đi nữa thì object sẽ được giải phóng, nên sẽ không bị memory leak.
Đối với nhiều object, thì chúng ta sử dụng một kĩ thuật như sau
Obj1 := nil;
Obj2 := nil;
Obj3 := nil;
try
Obj1 := TClass1.Create;
Obj2 := TClass2.Create;
Obj3 := TClass3.Create;
...
finally
Obj3.Free;
Obj2.Free;
Obj1.Free;
end;
Dù cho việc khởi tạo Obj nào bị lỗi đi nữa thì cả ba đều sẽ được giải phóng bộ nhớ.
Destroy là constructor dùng để phá hủy một đối tượng nào đó. Bạn có thể gọi Destroy với mọi object nào đó. Tuy nhiên, giải phóng đối tượng bằng Destroy là không an toàn bởi vì khi Destroy một object là nil, sẽ dẫn đến một lỗi Access Violation.
Do đó, mỗi class đều có thêm method Free. Free an toàn hơn Destroy, bởi vì nó kiểm tra object có phải nil không trước khi gọi Destroy.
Đây là cấu trúc method Free
procedure TObject.Free;
begin
if Self <> nil then
Self.Destroy;
end;
...
Obj.Free;
Sau khi Destroy hoặc Free được gọi, object vẫn chưa trả về nil. Điều này gây nhầm lẫn trong việc kiểm tra object có sử dụng được hay không (so sánh với nil).
begin
if Obj = nil
then writeln('Object khong su dung duoc')
else writeln('Object co the su dung');
end;
Ví dụ như trên, Obj đã được giải phóng bằng Free (hoặc Destroy), nhưng do Free không trả object về nil nên kết quả in ra là object co the su dung. Như vậy, chúng ta vô tình gây ra một lỗi vi phạm truy cập.
Unit System.SysUtils cung cấp một hàm FreeAndNil có chức năng tương tự như Free, nhưng gán object thành nil khi Free xong.
procedure FreeAndNil(var Obj);
begin
if Obj <> nil then
begin
Obj.Destroy;
Obj := nil;
end;
end;
...
FreeAndNil(Obj);
Đối với các object sử dụng lại nhiều lần (Create rồi Free rồi lại Create, ...) thì nên sử dụng FreeAndNil. Còn các object sử dụng một lần (Create một lần rồi Free) thì dùng Free cho gọn.
Trong lập trình giao diện (với VCL hoặc FMX Framework), quản lý bộ nhớ của các control (cũng là các object) được giao cho Owner. Có thể xem Owner như chủ sở hữu của control đó.
Nếu Owner bị hủy, thì các đối tượng do owner quản lý cũng bị hủy theo.
Khi control được tạo ra bằng constructor Create, thì owner là tham số đầu tiên được truyền vào Create.
Button1 := TButton.Create(Panel1);
...
Đoạn code trên tạo một Button1 bằng constructor Create với owner là Panel1. Cấu trúc khai báo của hầu hết các control như sau.
constructor TControl.Create(AOwner: TComponent);
Owner là một object TComponent.
Bạn cũng có thể tạo control với owner là nil. Khi đó, việc quản lý bộ nhớ bạn cần thực hiện thủ công (bằng try ... finally).