【问题标题】:Is there a need to set Objects to Nothing是否需要将 Objects 设置为 Nothing
【发布时间】:2009-02-05 17:47:44
【问题描述】:

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

无论将对象设置为Nothing,当离开函数范围时,引用是否丢失并释放内存?

即真的有必要吗:

Set db = Nothing
Set record_set = Nothing

【问题讨论】:

    标签: vba vbscript vb6


    【解决方案1】:

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

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

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

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

    例如,您读入一个文件,并在调用ReadAll() 之后立即将文件对象变量设置为Nothing。文件句柄将立即释放,您可以慢慢处理其内容。

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

    如果您不是处于“必须解除对有价值资源的阻塞”的情况,只需让变量超出范围即可。

    【讨论】:

    • 虽然您写的一切都是真实的(并且说得好),但问题被标记为 MS Access,这意味着 VBA。 Access 中的 VBA 在历史上一直存在无法正确更新引用计数的问题,因此建议在实践中明确清理对象变量。
    • 我不知道 VBA 和 VB 6.0 在这方面有任何可提及的区别。我不敢相信他们为 MS Access 编写了一个新的垃圾收集器和一个新的 VB 运行时。
    • 知识库文章并未指出 MS Access 中存在不同的垃圾收集器。它指的是 DAO 或 Access 和 DAO 之间的紧密联系中的一种特殊性,只有当 Access 用作自动化服务器时才会显现出来。
    • 这方面有什么权威的吗?我当然可以相信 GC 系统中可能存在错误,因此它无法正确识别对象何时符合 GC 条件。但问题是,VB 中是否真的存在一个错误,即超出范围的变量不会正确减少使用计数,但将变量设置为 none 会正确减少使用计数。我非常怀疑这样一种编程方法:“好吧,编译器可能会生成不正确的代码,所以我将编写一堆额外的代码以防万一。”这会在哪里...
    • @Tomalak 我在回复 cmets,而不是回复您的答案。 :-)
    【解决方案2】:

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

    出于这个原因,当我完成它们时,我明确地同时关闭并设置为Nothing记录集。

    【讨论】:

    • 您的意思是 .NET 垃圾收集并不完美,因为它的设计并不总是最优的,或者设计中存在错误?您是否有任何参考资料解释了建议您提早收集的情况?谢谢。
    • 有一天,一个学生来到 Moon 说:“我知道如何做一个更好的垃圾收集器。我们必须保留指向每个 cons 的指针的引用计数。”穆恩耐心地给学生讲了以下故事:“有一天,一个学生来到穆恩面前说:‘我知道如何做一个更好的垃圾收集器......
    • 我并不是说垃圾收集器在没有程序员干预的情况下永远无法工作,只是它并不完美,它可能需要一段时间才能清理干净。当您不再需要引用时,显式删除它们有助于垃圾收集器。
    • 感谢您的澄清。您显然是正确的,尽管有人可能会争辩说微优化并不总是值得的。
    • 是的...我永远不会回去审核/修复整个 100K 链接应用程序。但是,如果我正在编写新的代码,或者无论如何要修复其他的东西,我这样做是出于习惯。
    【解决方案3】:

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

    "Close 方法的替代方法是 设置对象变量的值 为空(设置 dbsTemp = 空)。”

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

    考虑到这一点,来自 Microsoft 知识库的题为“How to prevent database bloat after you use Data Access Objects (DAO)”的 this article 告诉您,如果您不希望您的数据库膨胀。你会注意到这篇文章对细节有点含糊; “原因”部分不清楚,几乎到了胡言乱语的程度。

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

    症状:Microsoft Access 数据库 已经开始膨胀(或在 大小)在您实施数据访问之后 对象 (DAO) 打开记录集。

    原因:如果您不发布 每次你记录集的内存 循环通过记录集代码,DAO 可能会重新编译,使用更多的内存和 增加数据库的大小。

    更多信息:当您创建一个 Recordset(或 QueryDef)对象 代码,显式关闭对象时 你完蛋了。微软访问 自动关闭 Recordset 和 大多数下的QueryDef对象 情况。但是,如果你 明确地关闭你的对象 代码,可以避免偶尔 对象保留时的实例 打开。

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

    【讨论】:

      【解决方案4】:

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

      因此,所有 MS VB 脚本示例总是显示对象被关闭并设置为空。这样脚本摘录就可以在对象没有超出范围的环境中使用,例如 ASP 经典。

      在极少数情况下,您希望编写长时间运行的进程,其中对象不会超出范围,并且如果您不显式释放对象,您会发现自己的物理内存不足。

      如果您发现自己在编写 ASP 经典代码,或者出于其他原因在全局范围内运行进程,那么是的,您应该显式释放对象。

      【讨论】:

        【解决方案5】:

        当变量超出范围时,应该清理引用。据推测,该软件的更高版本已对此进行了改进,但有时它并不可靠。我认为将变量显式设置为“无”仍然是一种好习惯。

        【讨论】:

          【解决方案6】:

          我通常总是把它放在我的程序的末尾,或者如果我使用的是模块级的,则调用一个“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属性检查它是否仍然打开(如果没有则关闭它),然后将其设置为nothing。这完全确保了它完全干净地关闭,并且可以在过程中的任何时间使用,无论 Recordset 是否已经打开,也不管是否没有。
          • 我的观点是,从 VBA 中使用的 ADO 没有任何 DAO 的参考问题。您正在清理 VBA 会可靠地为您清理的东西。也就是说,当然,假设首先使用 ADO 是有理由的,但通常情况下是没有的。
          【解决方案7】:

          试试这个

          If Not IsEmpty(vMyVariant) Then
              Erase vMyVariant
              vMyVariant = Empty
          End If
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-08-15
            • 1970-01-01
            相关资源
            最近更新 更多