GetRef 的内存消耗(垃圾收集)随 KB4525236 发生变化

GetRef's memory consumption (garbage collection) changed with KB4525236

我们在 Windows 2016 Servers/Windows 10 个客户端上安装 KB4525236 后遇到内存不足问题。此安全修复程序似乎更改了通过 GetRef.

调用函数时内存被垃圾回收的时刻

Pré KB4525236

在通过 GetRef 调用的函数中创建的每个实例在实例变量设置为 nothing

后立即被垃圾收集

Post KB4525236

在通过 GetRef 调用的函数中创建的每个实例都保留在内存中,并且 只有在整个函数完成时才收集垃圾。在循环中创建实例时,这会迅速累积并导致内存不足,尤其是在 32 位进程中。

问题

POC

安装了 KB4525236 的设备上的以下脚本 运行ning 显示了

时垃圾收集的差异

另存为:KB4525236.vbs
运行 为:wscript KB4525236.vbs

Dim Name, Log

Class IDummyInstance
  Dim FName
  Sub Class_Initialize
    FName = Name
    Log = Log & "Initialize " & FName & VbNewLine
  End Sub
  Sub Class_Terminate
    Log = Log & "Terminate " & FName & vbNewLine
  End Sub
End Class

Sub CreateDestroyTwoInstances
  Dim DummyInstance
  Name = "First Instance"
  Set DummyInstance = New IDummyInstance
  Set DummyInstance = Nothing
  Name = "Second Instance"
  Set DummyInstance = New IDummyInstance
  Set DummyInstance = Nothing
End Sub

Log = "(1) Direct Call :" & VbNewLine
Call CreateDestroyTwoInstances

Log = VbNewLine & Log & "(2) GetRef Call :" & vbNewLine
Set GetRefCall = GetRef ("CreateDestroyTwoInstances")
Call GetRefCall

MsgBox Log

由于我没有解决方案或官方来源来解释这个问题,所以我一直在等待赏金到期。

我想出了一个令人不愉快的解决方法,可以在错误修复之前提供帮助。

解决方法是不使用任何局部变量来保存可能通过 GetRef 执行的过程中的对象实例。

不使用隐式或显式变量,而是使用局部(如果没有递归则为全局)字典对象来保存对象实例并通过该字典调用它们。

Sub CreateDestroyTwoInstances
  Dim Refs
  Set Refs = CreateObject("Scripting.Dictionary")
  Name = "First Instance"
  Refs.Add "DummyInstance", New IDummyInstance
  ' Call Refs("DummyInstance").DoSomething()
  Refs.Remove "DummyInstance"
  Name = "Second Instance"
  Refs.Add "DummyInstance", New IDummyInstance
  ' Call Refs("DummyInstance").DoSomething()
  Refs.Remove "DummyInstance"
End Sub

如果你有一个不太复杂的脚本,似乎值得使用。

有一种迂回的方法可以让 VBScript 终止对象 'now' 而不是稍后。

通过将以下观察结果拼凑起来:

  • 如果调用 class 实例的成员函数,对象“清理”将延迟到函数的末尾。

  • 如果通过引用传递参数,您可以修改值(例如设置为 Nothing)。

你可能会得到这样的结果:

Class Disposal
   Public Sub Dispose(ByRef oRef)
      Set oRef = Nothing
   End Sub
End Class

' A global instance ready for use.
Dim GD: Set GD = New Disposal

要使用处置,您需要替换:

Set x = Nothing

与:

GD.Dispose x

下面是对将对象传递给 'dispose of' 时发生的情况的粗略解释:

  • Dispose 引用一个对象并将其设置为 Nothing(不言自明)。
  • 在函数结束时,VBScript 'sees' 不再有对该对象的引用。
  • VBScript 终止对象。

或者至少,这就是我认为它正在做的...

以下是应用于 POC 的解决方案:

Class Disposal
   Public Sub Dispose(ByRef oRef)
      Set oRef = Nothing
   End Sub
End Class

' A global instance ready for use.
Dim GD: Set GD = New Disposal

Dim Name, Log

Class IDummyInstance
  Dim FName
  Sub Class_Initialize
    FName = Name
    Log = Log & "Initialize " & FName & VbNewLine
  End Sub
  Sub Class_Terminate
    Log = Log & "Terminate " & FName & vbNewLine
  End Sub
End Class

Sub CreateDestroyTwoInstances
  Dim DummyInstance
  Name = "First Instance"
  Set DummyInstance = New IDummyInstance
  GD.Dispose DummyInstance 'Set DummyInstance = Nothing
  Name = "Second Instance"
  Set DummyInstance = New IDummyInstance
  GD.Dispose DummyInstance 'Set DummyInstance = Nothing
End Sub

Log = "(1) Direct Call :" & VbNewLine
Call CreateDestroyTwoInstances

Log = VbNewLine & Log & "(2) GetRef Call :" & vbNewLine
Set GetRefCall = GetRef ("CreateDestroyTwoInstances")
Call GetRefCall

MsgBox Log

希望这能帮助那些在 2021 年及以后仍然坚持使用 VBScript 的人...

好问题。我是通过 Microsoft 论坛找到它的 post

https://answers.microsoft.com/en-us/windows/forum/all/vbscriptdll-classterminate-bug-since-kb4524570/1b34d9b4-91ce-4d61-a05c-1bfa0ec96344?auth=1

这个 SO 的提问者在 Microsoft 线程上面添加了一个 link。

KB5005101 的发行说明中引用了该 Microsoft 线程(对于 Windows 10 21H1 - 其他 Windows 10 版本将有类似的 KB)。 Link 至 Microsoft 发行说明:https://support.microsoft.com/en-us/topic/september-1-2021-kb5005101-os-builds-19041-1202-19042-1202-and-19043-1202-preview-82a50f27-a56f-4212-96ce-1554e8058dc1

微软声称已经修复了这个问题 Addresses a memory leak that occurs when you use nested classes within VBScript. 在发行说明中,末尾的 VBScript 单词是对该 Microsoft 论坛主题的 hyperlink。比较微妙...

无论如何,虽然我不是 VBScript 的常客,但作为 COM 的长期用户,并且无论如何对 reader 这类问题感兴趣,我想我会分享最终应该是什么一旦每个人都获得了他们的每月补丁,就有了明确的答案。这些发行说明只能追溯到 Windows 10 2004,因此 1909 和更早的版本可能不走运,Windows 7 位用户可能会受此影响。