是否需要将“对象”设置为“无”


70

我总是读到,建议在完成对对象的设置后,将其设置为空。但是我通常只在表单内部的函数中使用它们。

无论将对象设置为Nothing,在离开函数作用域时是否都不会丢失参考并释放内存?

即是否真的有必要做:

Set db = Nothing
Set record_set = Nothing

Answers:


84

VB使用所谓的“引用计数”垃圾收集器。

基本上,当变量超出范围时,所引用对象上的引用计数器就会递减。当将对象引用分配给另一个变量时,引用计数器将增加。

当计数器达到零时,该对象已准备好进行垃圾回收。一旦发生这种情况,对象资源将被释放。函数局部变量很可能会引用其引用计数永远不会超过1的对象,因此在函数结束时将释放对象资源。

将变量设置为Nothing是显式减少参考计数器的方法。

例如,您读入一个文件,并将文件对象变量设置NothingReadAll()调用。文件句柄将立即释放,您可以花些时间处理其内容。

如果未设置为Nothing,则打开文件句柄的时间可能超过绝对必要的时间。

如果您不在“必须释放有价值的资源”这种情况下,只需让变量超出范围就可以。


21
尽管您编写的所有内容都是正确的(并且说得很对),但该问题的标签为MS Access,即VBA。从历史上看,Access中的VBA一直存在无法正确更新引用计数的问题,因此建议您在实践中明确清除对象变量。
David-W-Fenton,2009年

5
我不知道VBA和VB 6.0在这方面有什么可提及的区别。我不敢相信他们为MS Access编写了新的垃圾收集器和新的VB运行时。
Tomalak

5
KB文章并不表示MS Access中存在其他垃圾回收器。它指的是DAO或Access和DAO的紧密连接中的特殊性,只有将Access用作自动化服务器时才可以发现。
Tomalak

4
有什么权威吗?我当然可以相信,GC系统中可能存在错误,因此无法正确识别对象何时有资格使用GC。但问题是,VB中是否确实存在一个错误,使得超出范围的变量不能适当地减少使用计数,但是将变量设置为“无”确实可以适当地减少使用计数。我对一种编程方法表示怀疑,该方法说:“好吧,编译器可能会生成不正确的代码,所以我将写很多额外的代码,以防万一。” 这在哪里...
周杰伦

2
@Tomalak我是在回复评论,而不是您的答案。:-)
Jay

17

垃圾收集很少是完美的。即使在.NET中,有时也强烈建议您提示系统提早进行垃圾收集。

由于这个原因,当我处理完它们时,我会同时明确关闭并设置为Nothing记录集。


4
您是不是说.NET垃圾收集不是完美的,因为它的设计并不总是最佳的,或者设计中存在错误?您是否有任何参考资料说明建议您在什么情况下要求提早收款?谢谢。
布兰登·摩尔

1
有一天,一个学生来到月球说:“我知道如何做一个更好的垃圾收集器。我们必须保留指向每个缺点的指针的引用计数。” 穆恩耐心地向学生讲了以下故事:“有一天,一名学生来到穆恩说:'我知道如何做一个更好的垃圾收集器...
BIBD 2012年

我并不是说垃圾收集器在没有程序员干预的情况下将永远无法工作,只是因为它是不完善的,并且可能需要一段时间才能清理干净。当您不再需要引用时,将明确地摆脱引用对垃圾收集器的帮助。
BIBD 2012年

感谢您的澄清。您显然是正确的,尽管可能有人认为微优化并不总是值得的。
布兰登·摩尔

没错,我永远不会回过头来审核/修复整个100K链接应用程序。但是,如果我正在编写新的东西,或者无论如何要修复其他东西,我都会出于习惯而这样做。
BIBD 2012年

15

Microsoft DAO帮助和Access Developer参考中的“ Recordset.Close ”帮助主题的最后一行是这样的:

“ Close方法的一种替代方法是将对象变量的值设置为Nothing(设置dbsTemp = Nothing)。”

http://msdn.microsoft.com/en-us/library/bb243098.aspx

考虑到这一点,本文有权从Microsoft知识库“如何防止数据库膨胀您使用后的数据访问对象(DAO)”,告诉你,你应该明确地接近,如果你不想让你的数据库膨胀。您会注意到该文章对细节有些含糊。“原因”部分尚不清楚,几乎是乱码。

http://support.microsoft.com/kb/289562

症状:在实现数据访问对象(DAO)以打开记录集后,Microsoft Access数据库已开始膨胀(或大小快速增长)。

原因:如果您没有在每次遍历记录集代码时都释放记录集的内存,则DAO可能会重新编译,使用更多的内存并增加数据库的大小。

更多信息:当您在代码中创建一个Recordset(或QueryDef)对象时,完成后显式关闭该对象。在大多数情况下,Microsoft Access会自动关闭Recordset和QueryDef对象。但是,如果在代码中显式关闭对象,则可以避免在对象保持打开状态时偶尔出现实例。

最后,让我补充说,我已经使用Access数据库已有15年了,并且几乎总是让本地声明的记录集变量超出范围,而无需显式使用Close方法。我没有对其进行任何测试,但这似乎无关紧要。


4

当您使用ASP classic(服务器端脚本)时,将所有对象设置为空时很重要,因为在关闭[虚拟]服务器之前,它们不会超出范围。

因此,所有MS VB脚本示例都始终显示对象已关闭且未设置任何内容。因此,脚本摘录可以在对象不超出范围的ASP Classic等环境中使用。

在极少数情况下,您希望对长时间运行的进程进行编码,其中对象不会超出范围,并且如果您未明确释放对象,则会发现物理内存用完。

如果您发现自己正在编写ASP经典代码,或者由于某些其他原因在全局范围内运行进程,那么可以,则应显式释放对象。


3

当变量超出范围时,应该清理引用。大概在软件的更高版本中,此功能得到了改善,但它一次并不可靠。我认为将变量显式设置为“ Nothing”仍然是一个好习惯。


1

我通常总是将其放在过程的末尾,或者如果我使用模块级的,则在其中调用“ CloseRecordSet”子句:

Private Sub Rawr()
On Error GoTo ErrorHandler

    'Procedural Code Here.

    ExitPoint:
        'Closes and Destroys RecordSet Objects.
        If Not Recset Is Nothing Then
            If Recset.State = 1 Then
                Recset.Close
                Conn.Close
            End If
            Set Recset = Nothing
            Set Conn = Nothing
        End If
        Exit Sub

    ErrorHandler:
        'Error Handling / Reporting Here.
        Resume ExitPoint
End Sub

这样,过程结束了(无论是正常还是由于错误),对象将被清理,资源将被释放。

这样做很安全,因为您可以将其拍一下,并且只会关闭或销毁记录集/连接对象,如果它已经关闭(由于运行时错误或只需尽早关闭它,就可以确定)。

确实没什么麻烦,而且当您完成对象的清理以立即释放资源时,无论程序中发生了什么,清理对象始终是最好的。


那是ADO代码,不是吗?ADO记录集缺少State属性,因此您不使用连接对象。ADO没有DAO所具有的引用计数问题,因此您无需在以后进行清理。无论如何,这并不意味着您应该在Access应用程序中使用大量的ADO -在ADP之外,DAO是首选的数据访问库,除了ADO可以做的一些事情之外。
戴维·芬顿

它声明为ADODB.Recordset,并且具有定义其当前是否打开的state属性。基本上,它检查它是否已经设置为Nothing,如果不是,则首先使用state属性检查它是否仍处于打开状态(如果没有,则将其关闭),然后再将其设置为空。这完全确保了它可以完全干净地关闭,并且无论Recordset是否已打开,都可以在过程中的任何时间使用,无论是否已打开。
BobT

1
我的观点是,从VBA使用的ADO没有DAO遇到的任何参考问题。您正在清理VBA可以可靠地清理的内容。也就是说,当然,首先假设有使用ADO的合理性,但通常没有。
戴维·芬顿

-1

尝试这个

If Not IsEmpty(vMyVariant) Then
    Erase vMyVariant
    vMyVariant = Empty
End If
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.