当我在Delphi中释放可能不存在的内存时会发生什么?

4
Delphi没有垃圾回收器,所以对于有Java背景的人来说,这真是一件令人头痛的事情。
通常,为了释放我不再使用的某些内存,我会使用:
if (SomeMemory <> nil) then
  SomeMemory.Free

如果在删除之前不检查nil,会发生什么?

此外,为什么有人想要自己处理所有这些“垃圾收集”呢?为什么在Delphi的所有编译选项中,没有Garbage Collector = true


7
为什么在Delphi的所有编译选项中,没有“垃圾回收器=true”的选项呢?我有一个问题要问你,为什么不能在Java中禁用垃圾回收器? - David Heffernan
2
一个不那么轻浮的回答是,对于某些类型的应用程序来说,垃圾收集器会对您的应用程序施加不受欢迎的性能限制。 - David Heffernan
2
对于全局变量(例如 var Form1: TForm1),我建议您使用 FreeAndNil(Form1),以避免变量指向无效引用的问题。 - GabrielF
2
我在Programmers.SE上以Delphi开发人员的身份发布了一篇名为“不使用GC作为有意识的设计选择背后的哲学”的文章,这可能会澄清一些事情。 - Mason Wheeler
2
@MasonWheeler 这并不是一个“有意的设计选择”,而是旧技术。甚至Wirth自己在Oberon中也包含了垃圾回收,并为人们选择商业化他的“有缺陷”的早期作品而悲叹。没有一种21世纪创造的语言会忽略内存管理。这只是Delphi用户为了努力使自己相信Delphi仍然是唯一正确的方式而临时发明的东西。我知道,因为我曾经为接口/实现、Begin/End以及其他东西辩护。Delphi所没有的任何东西你实际上都不需要,或者实际上老的方式更好。 - alcalde
显示剩余10条评论
4个回答

7
< p > < code > TObject.Free 的代码如下:

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

因此,没有必要检查 nil。然而,如果尝试释放未初始化的变量,则会导致 AV 错误。检查 nil(或 Assigned)对此也毫无帮助。不过,如果您尝试这样做,编译器会发出警告。
回答您的第二个问题:

为什么 Delphi 拥有如此众多的编译选项,却没有垃圾回收器 = true?

简单来说,Delphi 并没有这样的垃圾回收器。某些受控类型(如字符串、动态数组等)实现了编译器管理的自动引用计数,在这些对象的引用计数降至零时,它们会被自动释放。对于所有其他未受控对象,开发人员需要负责在不需要时适当清理对象实例。
这不是关于是否“希望”管理应用程序的内存,而是必须做的事情。

在iOS和Android上,Delphi对象是使用ARC的编译器管理类型。 - Remy Lebeau
@RemyLebeau 这是FireMonkey的事情,还是特定于平台?所有对象都是吗?如果您正在编译跨平台应用程序,是否必须拥有IFDEF来有条件地释放Windows目标的对象? - J...
1
Object ARC是移动编译器的语言特性,目前桌面编译器不支持(虽然未来可能会支持)。它适用于所有TObject的子类。Embarcadero试图使ARC透明化,通过重新实现TObject.Free()FreeAndNil()以在ARC下工作,并在所有平台上引入TObject.DisposeOf()。如果您想编写支持ARC的代码(有时可能需要),可以使用相应的{$DEFINE}。对于大多数代码,您可以继续使用Free()并让ARC根据需要处理它。 - Remy Lebeau

6

在调用 Free 前检查是否为 nil 是多余的。如果引用是 nil,那么在它上面调用 Free 是安全的。如果引用不是 nil,那么在它上面调用 Free 的安全性并没有改变,而是完全取决于变量是否包含有效的引用。

例如:

SomeMemory := nil;
SomeMemory.Free; // This is safe.

SomeMemory := TObject.Create;
SomeMemory.Free; // This is safe
Assert(SomeMemory <> nil);
SomeMemory.Free; // This is an error (EInvalidOperation)

在第一个代码块中,我们没有检查变量是否为空,但是调用Free是完全安全的。它什么也不做。在第二个代码块中,我们看到变量仍然非空,但对其调用Free会导致异常。

4
在 Delphi 中,有一种非常直观的方法可以实现(类似)垃圾回收:使用接口指针。通过使用最终派生自 IInterface (或基本相同的 IUnknown)类型的变量,Delphi 将密切计算引用计数,并在删除最后一个引用时释放/销毁/释放实例。除了实例创建和其他一些事情之外,使用接口指针几乎与使用对象引用相同。
在 Nick Hodges 最近的作品 Coding In Delphi 中,有很多关于这种编程技术以及它与抽象、泛型、单元测试和依赖注入的关系的内容。
此外,其他平台的 Delphi 新版本 也将对对象引用进行自动引用计数(ARC),操作方式与接口现在所做的相同。(废弃 Free 方法,但这是另一个故事。)

1
当然。ARC 是未来。像 Java 和 C# 中的分代垃圾回收一样的东西是垃圾,废物,垃圾,不应该添加到 Delphi 中。 - Warren P
1
现在,现在,太消极了!我曾经有一个项目,其中ARC甚至都不够用,因为实例需要被强制销毁。我们使用“智能”指针,通过析构函数引发事件,并在异步使用它们时使用一种可中止的锁。总有一天我应该写一篇详细的博客文章... - Stijn Sanders

2
您可以随时查阅文档以回答此类问题。以下是相关内容,我加粗了部分内容:

使用Free销毁一个对象。如果对象引用不为nil,则Free会自动调用析构函数。在运行时实例化的任何没有所有者的对象都应该通过调用Free来销毁,以便可以正确处理并释放其内存。与Destroy不同,即使对象为nil,Free也是成功的; 如果从未初始化对象,则Free不会导致错误。

在这里找到更完整的讨论:为什么我不应该在使用或释放东西之前使用“if Assigned()”?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接