在我的Delphi7中这段代码
var MStr: TMemoryStream;
...
FreeAndNil(MStr);
MStr.Size:=0;
在模块“Project1.exe”中的地址0041D6D1处生成AV:访问冲突。读取地址00000000。 但有人坚持认为不管怎么说都不应该提出任何例外。他还说他的Delphi 5确实没有例外。他称之为“陈旧的指针错误”。 换句话说,他说FreeAndNil不能用作调试器来检测释放对象或使用释放对象的双重尝试。
任何人都可以开导我吗?如果这个引发错误(总是/随机)或程序应该没有问题地运行这个错误?
由于
我问这个是因为我相信我的程序中有一个“双重免费对象”或“免费且重新访问”的错误。在释放对象后,如何用零填充分配给对象的内存?我希望这种方式通过获取和AV来检测bug的位置。 最初,我希望如果我将对象设置为FreeAndNil,我将在尝试重新访问它时始终获得AV。
答案 0 :(得分:21)
使用null引用的方法或属性总是错误的,即使它似乎有时会起作用。
FreeAndNil
确实无法用于检测双重释放。在已经为零的变量上调用FreeAndNil
是安全的。由于它是安全的,它无助于您检测任何东西。
这不是一个陈旧的指针错误。这是一个空引用错误。一个陈旧的指针错误就是你释放了一个对象,但不清除了所有引用它的变量。然后变量仍然保存对象的旧地址。那些很难察觉。你可以得到这样的错误:
MStr := TMemoryStream.Create;
MStr.Free;
MStr.Size := 0;
你也可以这样:
MStr := TMemoryStream.Create;
OtherStr := MStr;
FreeAndNil(MStr);
OtherStr.Size := 0;
在释放引用的对象MStr.Size
后使用MStr
是一个错误,它应该引发异常。 是否引发异常取决于实现。也许它会,也许它不会。但这不是随机的。
如果您正在搜索双重免费错误,则可以使用FastMM提供的调试助手,正如其他人所建议的那样。它的工作原理是不将内存释放回操作系统,甚至回到Delphi的内部空闲内存池。相反,它将已知坏数据写入对象的内存空间,因此当您看到这些值时,您将知道您正在读取已经释放的内容。它还修改了对象的VMT,以便下次在该对象引用上调用虚方法时,您将获得可预测的异常,它甚至会告诉您尝试使用哪个被释放的对象。当您尝试再次释放该对象时,它不仅可以告诉您已经释放它,还可以告诉您第一次释放它(使用堆栈跟踪)以及分配它的位置。它还收集该信息以报告有关内存泄漏的信息,其中您释放的对象 less 而不是更多。
您可以使用习惯来避免将来的代码出现问题:
FreeAndNil
,那么仍然保持其他代码的变量不变。如果其他代码认为它拥有该对象,那么您就遇到了麻烦。 (所有者的概念不一定与TComponent.Owner
属性相关联。不需要拥有它的对象;它可以是程序的一般子系统。)< / LI>
答案 1 :(得分:10)
从我所看到的,这段代码应该总是导致错误。 FreeAndNil显式地将传递的值设置为Nil(也称为0),因此在尝试取消引用对象时,绝对应该获得访问冲突。
答案 2 :(得分:8)
只是让问题复杂化:
如果您调用的方法是静态(非虚拟)方法并且它本身不调用任何虚方法,也不访问该对象的任何字段,即使已设置对象引用,也不会出现访问冲突到NIL。
原因是访问冲突是由解除引用自指针(在本例中为NIL)引起的,但只有在访问字段或对象的VMT以调用虚方法时才会发生。
这只是规则的一个例外,你不能调用我想在这里提到的NIL对象引用的方法。
答案 3 :(得分:5)
如果将指针设置为nil,则不应再使用它。但是如果你有另一个指向同一个对象的指针,你可以使用它而不需要AV,因为这个指针仍指向对象地址而不是nil。
此外,释放对象不会清除该对象使用的内存。它只是标志着它没有被使用。这就是你想要AV的原因。如果为另一个对象分配了释放的内存,您将获得一个AV,因为它不再包含似乎有效的数据。
FastMM4有一些可以在调试时使用的设置,可以检测这些情况。来自FsatMM4Options.inc:
{设置以下选项以对所有内存块进行大量检查。所有 块用标题和尾部填充,用于验证 堆的完整性。释放的块也被清除以确保它们 被释放后不能重复使用。此选项会降低内存操作速度 显着,应该只用于调试应用程序 覆盖内存或重用已释放的指针。设置此选项 自动启用CheckHeapForCorruption并禁用ASMVersion。 非常重要:如果启用此选项,您的应用程序将需要 FastMM_FullDebugMode.dll库。如果没有这个库,你会的 启动时出错。}
{$ define FullDebugMode}
同一档案的另一个引用:
FastMM总是捕获两次释放相同内存块的尝试......
由于delphi使用Delphi 2007(2006?)中的FastMM,如果你尝试双重对象,你应该会收到错误。
答案 4 :(得分:1)
Thomas Mueller :您尝试过虚拟类方法吗?构造函数是一种虚方法,但您可以根据类型调用它 - 而不是实例。这意味着即使某些特定的虚拟方法也不会在空引用上引起AV:D
Vegar :你不可能更正确! FastMM是有史以来最好的工具,帮助我追踪这种错误。
答案 5 :(得分:1)
EurekaLog博客在2009年4月发表了一篇很好的文章: