【问题标题】:VBA memory issue while assigning Range with array用数组分配 Range 时出现 VBA 内存问题
【发布时间】:2012-04-30 10:06:34
【问题描述】:

我的目的是为一个 Excel 单元格区域分配一个二维变量数组的值。

分配工作正常,但我面临内存问题(泄漏):当我启动 Excel 时,该过程需要或多或少 22 Mo 的 Ram。运行以下代码后(创建新工作簿、分配范围、关闭工作簿而不保存),该过程占用 33 Mo 的 RAM。

有人知道我做错了什么吗?

问候, 亚历山大

这里是代码

Option Explicit

Sub test()
   Dim nLines As Long
   Dim xSize As Long
   Dim j As Long
   Dim i As Long

   'Creating New Empty Workbook
   Application.Workbooks.Add
   Range("A1").Select

   Application.ScreenUpdating = False

   nLines = 10000
   xSize = 200

   Dim myRange As Range
   Dim myArray() As Variant

   ReDim myArray(1 To nLines, 1 To xSize)

   'Assigning some values
   For j = 1 To nLines:
       For i = 1 To xSize:
        myArray(j, i) = i * 4 / 3#
       Next i
   Next j

   Set myRange = Range(ActiveCell, ActiveCell.Offset(nLines - 1, xSize - 1))
   myRange .Value = myArray

   'Cleaning up
    Erase myArray
    Set myRange = Nothing

    'Closing workbook without saving
    Application.ActiveWorkbook.Close (False)

    Application.ScreenUpdating = True

End Sub

【问题讨论】:

  • 不确定 Mo 是什么,但是即使没有 VBA,运行 Excel 时 11 MB 无法解释的 RAM 也很常见...
  • Does anyone have an idea what I am doing wrong ? 你没有做错任何事 :) 33-45 mb 的内存来处理你正在创建的数组是很正常的 :) 这是一篇关于“Excel 内存限制 - Charles威廉斯”,您可能喜欢阅读? decisionmodels.com/memlimitsc.htm
  • 感谢您的回答。 11 MB(法语为 Mo,抱歉),我同意这不是一个重要的泄漏,但我发布的代码只是我必须为非常大的范围做的一个示例。如果我设置 xSize=2000 而不是 200,我会得到 100 MB 的泄漏...逐行调试代码显示指令“Set myRange = Nothing”不会释放任何内存。有什么想法吗?
  • FWIW,我在 Excel 2010 32 位、Win7 64 位中使用这些结果运行此代码:在运行宏之前打开工作表并添加此代码 - 31Meg,在 .Close 行上运行宏直到中断 - 52Meg,让宏完成 - 31Meg。即no内存泄漏
  • 我在 Win7 32 位上运行 Excel 2007 32 位。我第一次打开 Excel 并中断“Application.ScreenUpdating = False”语句,22 MB,运行到中断。关闭行 50 MB,让宏完成 34 MB。第二次重新启动宏不再显示任何泄漏,内存使用稳定在 34 MB。感谢您的帮助,但我还是有点担心(不知道到底发生了什么)。

标签: excel vba memory range memory-leaks


【解决方案1】:

VBA 不是 C,不要指望能够以任何方式控制 Excel 的内存占用。 Excel 本身并不擅长内存管理,而且通常情况下,您正在运行宏或在这些宏中所做的事情几乎无法控制 Excel 决定分配多少内存,或者它是否曾经决定再次释放它。

话虽如此,您可以在代码中做一些事情来减少它的混乱。

  1. Range("A1").Select

    在 Excel 中对单元格进行操作之前选择单元格通常是一种不好的做法。唯一需要选择单元格的情况是,如果您正在向宏的查看者说明某些内容。此外,此代码实际上可能会在某些版本的 excel 中引起问题,因为您刚刚添加了一个工作簿,并且没有什么可以告诉 excel 哪个工作簿,以及它应该从该工作簿中选择哪个工作表。您应该完全删除此代码(无论如何它不会影响您的逻辑)。

  2. Set myRange = Range( ...

    此代码不是必需的,因为您只对范围执行一项操作。您根本不需要创建 myRange 变量,因此您也不必担心将其设置为空,或者更重要的是 - 将其设置为空这一事实实际上并没有在内存方面完成任何事情。

  3. ActiveCellApplication.ActiveWorkbook

    同样,这些基于对“Select”和“ActiveWorkbook”调用的非限定引用并不明确,并且可能导致不可靠的行为,尤其是当用户在同一个 excel 实例中打开其他电子表格时。您最好存储对您在开始时添加的工作簿的引用。

总而言之,我会将您的代码更改为:

Sub test()
    Const nLines As Long = 10000, xSize As Long = 200
    Dim i As Long, j As Long
    Dim newWorkbook As Workbook
    
    Application.ScreenUpdating = False
    
    'Creating New Empty Workbook
    Set newWorkbook = Application.Workbooks.Add
    
    'Assigning some values
    Dim myArray(1 To nLines, 1 To xSize) As Double
    For j = 1 To nLines:
       For i = 1 To xSize:
        myArray(j, i) = i * 4 / 3#
       Next i
    Next j
    
    With newWorkbook.Sheets(1)
        .Range(.Cells(1, 1), .Cells(nLines - 1, xSize - 1)).Value = myArray
    End With
    
    'Closing workbook without saving
    newWorkbook.Close (False)
    
    'Cleaning up
    Erase myArray
    Set newWorkbook = Nothing
        
    Application.ScreenUpdating = True
End Sub

结束语:

除了 VBA 编码课程 - 请注意,打开新工作簿的行为会要求 Excel 分配大量新内存 - 比创建临时数组要多得多。即使在您关闭新工作簿之后,这些内存也可以释放或保留。将无法绕过这些类型的增长。请注意,Excel 的内存占用不仅仅与您当前的工作簿相关,它是实例的整个生命周期(支持多个工作簿)及其备份、优化的计算树、撤消历史记录等。将新启动的 excel 实例的足迹与刚刚完成“一堆东西”的实例的足迹进行比较。

还请注意,Excel 并未决定释放该内存,因此不会使其泄漏。 Excel 可能已在可回收/可重用对象中分配内存,这些对象将被您决定打开的任何下一个工作簿重新填充。反复重新运行代码可能会导致每次内存小幅增加,但 Excel 也可以跟踪一定数量的实例操作历史记录。

为了(有趣地)说明我的观点,这是 Excel (2007) 在打开一个新实例并运行您的代码一次、9 次以上、然后再运行 40 次之后的内存占用情况。

这不是你的代码的错,你没有责任担心它,如果你的业务用户担心它,他们不应该选择旧版本的 Excel 作为他们的目标开发平台。

【讨论】:

  • +1 表示“VBA 不是 C”和 taskmgr 屏幕截图。很好的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-23
  • 2020-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-28
  • 1970-01-01
相关资源
最近更新 更多