VBScript中对象销毁的顺序是什么?

7

.vbs中对象的销毁顺序是什么?

也就是说,给定以下全局变量:

Set x = New Xxx
Set y = New Yyy

我对以下任何问题的答案都感兴趣。

  1. 在.VBS中实现的类的实例中,Class_Terminate将按照什么顺序被调用?初步探索表明是按创建的顺序(而不是反向顺序!),但这是否有保证?

    编辑:我知道当最后一个对象引用被释放时,将会调用Class_Terminate。我的意思是:x和y将以什么顺序被释放,并且这是否有保证?为简单起见,假设x和y是它们各自对象的唯一引用。

  2. 对象的类型是否重要?例如,如果我在.VBS中实现的类与其他COM对象混合在一起,例如Scripting.FileSystemObject

    编辑:我知道COM库可能设置自己的内部循环引用,脚本主机引擎对此一无所知;我想探索影响第一个问题答案的因素。

  3. 如果x和y是局部变量而不是全局变量,上述答案是否有所不同?

  4. 是否取决于退出方式是正常退出、异常退出还是通过WScript.Quit退出?(在后一种情况下,似乎仍会在退出之前调用Class_Terminate,但这可能导致报告错误)。

  5. WScript对象何时被销毁?

  6. 脚本主机是否重要?(wscript.exe与cscript.exe相比以及Web主机引擎的任何名称)

  7. JScript的对象销毁模型是否与VBScript的不同?

我可以通过实证找到一些问题的答案,但我对其中是否有任何问题是保证 / 文档化感兴趣。

即使您只知道其中一些答案或其他相关问题,请发帖。


注意:这源于我在回答另一个问题时提出的有些hacky的建议。 - bacar
Eric Lippert在他的博客上写了几篇关于VBScript终止符的文章:* T4: VBScript and the Terminator * VBScript Terminators, Part Two - Mark Cidade
我不知道它们被称为“终止符” - 我搜索了很多“析构函数”、“销毁”等关键词,但都没有找到答案 :) 谢谢 - 我会阅读并点赞的,如果他们回答了这个问题。 - bacar
这些文章涉及到“未终止”的对象(例如由于循环引用而导致的),但不涉及更简单的情况。我已经修改了我的问题,使它更清晰一些。 - bacar
1个回答

11

我用VBScript设计并实现了这个特性。

大部分答案都在我文章中,Mark引用了它们,但是为了澄清:

Class_Terminate会以什么顺序调用?

一般来说,在对象的最后一个引用被释放时,终结器会立即被调用。然而,由于循环引用和其他问题,依赖确定性终止顺序通常是一个非常糟糕的主意。

简单试探表明其按创建顺序(不是相反的顺序!)进行排序,但这是有保证的吗?

正如我在文章中指出的那样,当引擎关闭时,未终止的对象将被终止。作为实现细节,终止队列会按对象的创建顺序执行。然而,这是一个未经记录的实现细节,你不应该依赖它。

对象的类型是否重要?例如,如果我在 .VBS 中实现了类并将其与 Scripting.FileSystemObject 等其他 COM 对象混合使用。

可能会有影响。这些对象之间可能存在循环引用,导致它们在无法预测的时间被拆除。

我在考虑全局范围内的对象,当程序退出时——对于函数范围内的对象是否不同?

我不理解这个问题。你能澄清一下吗?

这是否取决于退出是正常的、通过异常还是通过 WScript.Quit?(在后一种情况下,似乎仍会调用任何未解决的对象的 Class_Terminate,但这可能会导致报告错误)。

可能会有影响,是的。VBScript 不保证终结器总是运行。拥有引擎的宿主可以通过 "快速失败" 的方式关闭其进程,这种方式不能保证干净地关闭引擎,例如。在发生灾难性故障的情况下,有时这是可取的;如果不知道出了什么问题,有时运行终止代码会使问题变得更糟。

当调用 Quit 方法时,Windows Script Host 会尝试清理引擎并正常关闭。

WScript 对象何时被销毁?

当 Windows Script Host 进程终止逻辑运行时。

脚本宿主程序是否重要?(wscript.exe vs cscript.exe vs. 网页宿主程序等)

是的,它很重要。

JScript 的对象销毁模型与 VBScript 的有何不同?

非常不同。

我在从事 JScript "Classic" 开发期间(2001 年之前)使用的是一种非确定性标记-清除垃圾收集器,它可以处理脚本对象之间的循环引用,但不能处理脚本对象和浏览器对象之间的循环引用。JScript "Classic" 的较新版本具有改进的垃圾收集器,其可以处理脚本对象和浏览器对象之间的循环引用(但不一定能检测到涉及 JScript 对象和第三方 ActiveX 对象之间的循环引用)。

IE 9 版本的 JScript 具有完全重写的垃圾收集器,使用了非常不同的技术; 我与其设计者进行了一些交谈,但我没有足够的技术知识来深入讨论其特性。

JScript .NET 当然使用 CLR 垃圾收集器。

我可以问问你为什么关心所有这些问题吗?

另外,请注意我已经十多年没看过这段代码了;请以适当的怀疑态度对待这些信息。我的记忆可能不准确。


Eric,感谢您抽出时间提供详细和建设性的答案。您的回答(以及文章)解决了关于如何在关闭时处理未被释放的对象 - 由于循环引用(“和其他问题”)而未被释放的对象。否则,“终结器……在最后一个对对象的引用被释放时立即调用”。但是,在没有循环引用的情况下,当处于相同作用域的两个引用超出作用域时,它们以什么顺序被释放?我希望这是一个更清晰的问题,并解决了有关函数/全局的混淆。我将更新我的问题以澄清。 - bacar
"WScript对象会在WSH进程终止逻辑运行时被销毁" - 在这种情况下,“进程终止逻辑”是什么?是在所有用户代码(包括终止器)运行之后执行的东西吗?我关心这个问题,因为1,我通常很感兴趣了解底层实现(并知道什么是保证的而不是实现细节);2. 如我在对问题的评论中提到的那样,我建议使用终止器来获取未处理错误的非0退出代码 - 在您的答案中,我正在快速修订该解决方案! - bacar
@bacar:在终止程序之前,不能销毁WScript对象,因为终止程序可能会调用WScript对象的方法。 - Eric Lippert
没错 - 我只是想确认一下 :) - bacar
有没有可能更新一下回答最新的问题(和最新的评论)?这样我就可以接受你的答案并继续前进了 :) - bacar

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