在我的Delphi7中这段代码
var MStr: TMemoryStream; ... FreeAndNil(MStr); MStr.Size:=0;
在模块"Project1.exe"中的地址0041D6D1处生成AV:访问冲突.读取地址00000000.但是有人坚持认为不管怎么说都不应该引起任何例外.他还说他的Delphi 5确实没有例外.他称之为"陈旧的指针错误".换句话说,他说FreeAndNil不能用作调试器来检测释放对象或使用释放对象的双重尝试.
任何人都可以开导我吗?如果这个引发错误(总是/随机)或程序应该没有问题地运行这个错误?
谢谢
我问这个是因为我相信我的程序中有一个"双重免费对象"或"免费且重新访问"的错误.在释放对象后,如何用零填充分配给对象的内存?我希望这种方式通过获取和AV来检测bug的位置.最初,我希望如果我将对象设置为FreeAndNil,我将在尝试重新访问它时始终获得AV.
使用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,以便下次在该对象引用上调用虚方法时,您将获得可预测的异常,它甚至会告诉您尝试使用哪个被释放的对象.当您尝试再次释放该对象时,它不仅可以告诉您已经释放它,还可以告诉您第一次释放它(使用堆栈跟踪)以及分配它的位置.不到一次而不是更多.
您还可以使用习惯来避免将来的代码问题:
减少全局变量的使用.整个程序中的任何代码都可以修改一个全局变量,强迫您在使用它时不知道,"这个变量的值是否仍然有效,或者其他一些代码是否已经释放它?" 限制变量的范围时,在查找变量没有预期值的原因时,可以减少程序中必须考虑的代码量.
明确谁拥有一个对象.当有两段代码可以访问同一个对象时,您需要知道哪些代码片段拥有该对象.它们可能每个都有一个不同的变量用于引用对象,但是那里仍然只有一个对象.如果一段代码调用FreeAndNil
它的变量,那么仍然保持其他代码的变量不变.如果其他代码认为它拥有该对象,那么您就遇到了麻烦.(所有者的概念不一定与TComponent.Owner
属性相关联.不需要拥有它的对象 ;它可以是程序的一般子系统.)
不要对您不拥有的对象保持持久引用.如果不保留对对象的长期引用,则不必担心这些引用是否仍然有效.唯一的持久引用应该在拥有该对象的代码中.需要使用该对象的任何其他代码都应该接收引用作为输入参数,使用该对象,然后在返回其结果时丢弃该引用.
从我所看到的,这段代码应该总是导致错误.FreeAndNil显式地将传递的值设置为Nil(也称为0),因此在尝试取消引用对象时,绝对应该获得访问冲突.
只是为了使问题复杂化:
如果您调用的方法是静态(非虚拟)方法,并且它不调用任何虚方法本身,也不访问该对象的任何字段,即使对象引用已设置为NIL,也不会出现访问冲突.
原因是访问冲突是由解除引用自指针(在本例中为NIL)引起的,但这只发生在访问字段或对象的VMT以调用虚方法时.
这只是规则的一个例外,你不能调用我想在这里提到的NIL对象引用的方法.
如果将指针设置为nil,则不应再使用它.但是如果你有另一个指向同一个对象的指针,你可以使用它而不需要AV,因为这个指针仍指向对象地址而不是nil.
此外,释放对象不会清除该对象使用的内存.它只是标志着它没有被使用.这就是你想要AV的原因.如果为另一个对象分配了释放的内存,您将获得一个AV,因为它不再包含似乎有效的数据.
FastMM4有一些可以在调试时使用的设置,可以检测这些情况.来自FsatMM4Options.inc:
{设置以下选项以对所有内存块进行大量检查.所有块都用标题和尾部填充,用于验证堆的完整性.释放的块也被清除,以确保它们在被释放后不能被重用.此选项会显着减慢内存操作,并且只应用于调试覆盖内存或重用已释放指针的应用程序.设置此选项会自动启用CheckHeapForCorruption并禁用ASMVersion.非常重要:如果启用此选项,您的应用程序将需要FastMM_FullDebugMode.dll库.如果此库不可用,您将在启动时收到错误.}
{$ define FullDebugMode}
来自同一文件的另一个引用:
FastMM总是捕获两次尝试释放相同的内存块...
由于delphi使用Delphi 2007(2006?)中的FastMM,如果你试图双重一个对象,你应该得到一个错误.