You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用.NET 3.5 Hashtable时Excel VBA对象无法正常释放的问题

.NET 3.5 Hashtable/SortedList持有VBA对象导致内存泄漏的解决办法

问题说明

使用.NET 3.5的System.Collections.Hashtable或SortedList添加VBA类实例后,即便调用Clear、释放集合对象,VBA类的Terminate事件也不会触发,说明对象未被释放,出现了内存泄漏。VBA自带的Dictionary能正常释放对象,但找不到替代SortedList的合适方案。

测试代码

最简测试类(TestObject)

Option Explicit

Private Sub Class_Initialize()
    Debug.Print "Initialize"
End Sub

Private Sub Class_Terminate()
    Debug.Print "Terminate"
End Sub

正常释放的对照测试

Option Explicit

Sub Test()
    Dim oTestObject As TestObject
    Dim oTestObject2 As TestObject
    
    Set oTestObject = New TestObject
    Set oTestObject2 = oTestObject
    
    Set oTestObject = Nothing
    Set oTestObject2 = Nothing
End Sub

运行后即时窗口先显示Initialize,最后显示Terminate,对象释放正常。

Hashtable泄漏测试

Option Explicit

Sub Test()
    Dim oTestObject As TestObject
    Dim oTestObject2 As TestObject
    Dim ht As Hashtable
    
    Set oTestObject = New TestObject
    Set oTestObject2 = oTestObject

    Set ht = New Hashtable
    ht.Add "key", oTestObject
    
    Set oTestObject = Nothing
    Set oTestObject2 = Nothing
    
    ht.Clear
    Set ht = Nothing
End Sub

运行后即时窗口始终不显示Terminate,对象未被释放。

SortedList同样泄漏

Option Explicit

Sub Test()
    Dim oTestObject As TestObject
    Dim oTestObject2 As TestObject
    Dim sl As SortedList
    
    Set oTestObject = New TestObject
    Set oTestObject2 = oTestObject
    Set sl = New SortedList
    
    sl.Add "key", oTestObject
    
    Set oTestObject = Nothing
    Set oTestObject2 = Nothing
    sl.Clear
    Set sl = Nothing
End Sub

同样无法触发Terminate事件。

解决办法

1. 手动解除集合对VBA对象的引用

在调用Clear或释放集合之前,先把集合中对应键的对象显式设为Nothing,强制断开.NET集合的引用:

修复Hashtable的代码

Sub TestFixedHashtable()
    Dim oTestObject As TestObject
    Dim oTestObject2 As TestObject
    Dim ht As Hashtable
    
    Set oTestObject = New TestObject
    Set oTestObject2 = oTestObject

    Set ht = New Hashtable
    ht.Add "key", oTestObject
    
    Set oTestObject = Nothing
    Set oTestObject2 = Nothing
    
    ' 先手动把集合里的对象置空
    ht("key") = Nothing
    ht.Clear
    Set ht = Nothing
End Sub

修复SortedList的代码

Sub TestFixedSortedList()
    Dim oTestObject As TestObject
    Dim oTestObject2 As TestObject
    Dim sl As SortedList
    
    Set oTestObject = New TestObject
    Set oTestObject2 = oTestObject
    Set sl = New SortedList
    
    sl.Add "key", oTestObject
    
    Set oTestObject = Nothing
    Set oTestObject2 = Nothing
    
    ' 手动置空引用
    sl("key") = Nothing
    sl.Clear
    Set sl = Nothing
End Sub

操作后即时窗口会正常显示Terminate,对象被正确回收。

2. 自定义VBA版有序列表替代SortedList

如果不想每次手动置空,可以自己实现一个基于VBA的有序列表,确保引用能被正确释放:

Class SortedVBAList
    Private m_items As Collection
    
    Private Sub Class_Initialize()
        Set m_items = New Collection
    End Sub
    
    Private Sub Class_Terminate()
        ' 销毁时手动释放所有对象引用
        Dim item As Variant
        For Each item In m_items
            Set item(1) = Nothing
        Next
        Set m_items = Nothing
    End Sub
    
    Public Sub Add(key As String, value As Variant)
        ' 插入时保持键的升序排列
        Dim i As Integer
        For i = 1 To m_items.Count
            Dim existingKey As String
            existingKey = m_items(i)(0)
            If key < existingKey Then
                m_items.Add Array(key, value), Before:=i
                Exit Sub
            End If
        Next
        m_items.Add Array(key, value)
    End Sub
    
    Public Property Get Item(key As String) As Variant
        Dim i As Integer
        For i = 1 To m_items.Count
            If m_items(i)(0) = key Then
                Set Item = m_items(i)(1)
                Exit Property
            End If
        Next
        Err.Raise 9, , "键不存在"
    End Property
    
    Public Sub Clear()
        Dim item As Variant
        For Each item In m_items
            Set item(1) = Nothing
        Next
        m_items.Clear
    End Sub
End Class

使用这个自定义类时,Clear或类销毁时会自动释放所有对象引用,不会出现泄漏问题。

原因分析

.NET的Hashtable和SortedList在处理COM对象(VBA类属于COM对象)时,内部引用计数管理与VBA垃圾回收机制存在兼容性问题。直接调用Clear仅移除集合条目,但不会主动清空COM对象的引用,导致VBA垃圾回收器无法检测到对象已无引用,无法触发Terminate事件。手动置空引用能强制解除.NET集合的持有,让VBA正常回收对象。

内容的提问来源于stack exchange,提问作者mbmast

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 15:17:55