3
Option Explicit
Dim VBFlexGridCells() As String

Private Sub store_values()
On Error GoTo store_values_EH

Dim IndexLong As Long
Dim i As Integer
Dim entry As String
Dim rw As Integer

Dim aa As String
Dim bb As String
Dim cc As String
Dim dd As String
Dim ee As String
Dim jj As Integer
Dim ff As String
Dim ll As String
Dim mm As String
Dim nn As String
Dim kk As String

rw = 1
For rw = 0 To 15000
    aa = String(10000, "w")
    bb = String(125, "w")
    cc = String(125, "w")
    dd = String(125, "w")
    ee = String(125, "w")
    ff = String(125, "w")
    kk = String(125, "w")
    ll = String(125, "w")
    mm = String(125, "w")
    nn = String(125, "w")


    entry = aa & Chr(9) & bb & Chr(9) & cc & Chr(9) & dd & Chr(9) & ee & Chr(9) & ff & Chr(9) & kk & Chr(9) & ll & Chr(9) & mm & Chr(9) & nn

    IndexLong = IndexLong + 1
    ReDim Preserve VBFlexGridCells(10, IndexLong)

    Dim abc() As String
    abc = Split(entry, vbTab)
    For jj = LBound(abc) To UBound(abc)
        VBFlexGridCells(jj, IndexLong) = abc(jj)
    Next

Next

Exit Sub
store_values_EH:
MsgBox Err.Description & Space(10) & "store_values"
End Sub

Before the execution of the code above, the memory used by our vb6 program :17,720 KB

After the execution of the code above , the memory used by our vb6 program :386,836 KB

Our approximation of memory usage after code execution :320 MB enter image description here Actual memory usage and approximated memory usage are in same range ~300MB

however, when string cc is increased to take in 126 chars there is huge spike in memory usage.

cc = String(126, "w")

The memory used by our vb6 program after code execution:700.04 MB

Our approximation of memory usage after code execution :320 MB

enter image description here Memory usage shoots from 320 Mb to 700 MB .similarly, when other strings are increased to 126 chars , memory shoots up in the range of GB's and results in "out of memory" errors.Also there seems to be some issue with vb6 string append (http://www.aivosto.com/articles/stringopt2.html "Building huge strings" )

Is there any option to detect and clear (de-allocate) this unused memory in vb6?

IT researcher
  • 3,274
  • 17
  • 79
  • 143
  • What happens when you remove `redim preserve` and allocate the entire `VBFlexGridCells(10, 15000)` at once? – GSerg Jan 04 '19 at 09:57
  • @Gserg no change, will shoot up to 700 MB. – IT researcher Jan 04 '19 at 10:26
  • It almost looks like VB6 uses an optimized way of handling strings if the entire length in bytes (`4 + chars_count*2 + 2`) is not greater than 256, but that is weird because the strings in question do not change and do not get stored directly, it's the `entry` that should matter, but its length appears to not matter (e.g. if you increase `cc` to 126 but decrease `dd` to 124 you will still get double memory consumption even though the `entry` length stays the same). There does not appear to be leaked memory either, because when you `Erase VBFlexGridCells`, all memory comes back. – GSerg Jan 04 '19 at 10:59
  • @GSerg for `aa` to 16000 ,`cc` to 125 , memory usage is 500 MB and then increasing `cc` to 126 , the memory usage is 1 GB. For `aa` to 16500, `cc` to 125, the memory usage is 530 MB but then the increase `cc` to **126,the memory usage is 530 MB only.** – IT researcher Jan 04 '19 at 12:00

2 Answers2

0

I don't really know what is going on, but it has to do something with internals of what entry ends up containing, and how that is written to the array. Somehow the array ends up consuming more memory if it was constructed while entry was generated the way it was - and the array is the only thing that may have memory allocated to it, there is nothing "leaked".

To better demonstrate the problem, let's remove the code we don't need, streamline the array bounds declaration and move everything except the entry assignment out of the loop:

Private Sub store_values()
  Dim entry As String
  Dim rw As Long

  Dim aa As String
  Dim bb As String
  Dim cc As String
  Dim dd As String
  Dim ee As String
  Dim jj As Long
  Dim ff As String
  Dim ll As String
  Dim mm As String
  Dim nn As String
  Dim kk As String

  aa = String(10000, "w")
  bb = String(125, "w")
  cc = String(126, "w") ' Increased from 125 to 126
  dd = String(125, "w")
  ee = String(125, "w")
  ff = String(125, "w")
  kk = String(125, "w")
  ll = String(125, "w")
  mm = String(125, "w")
  nn = String(125, "w")

  ReDim VBFlexGridCells(0 To 9, 1 To 15000)

  For rw = 1 To 15000
      entry = aa & Chr(9) & bb & Chr(9) & cc & Chr(9) & dd & Chr(9) & ee & Chr(9) & ff & Chr(9) & kk & Chr(9) & ll & Chr(9) & mm & Chr(9) & nn

      Dim abc() As String
      abc = Split(entry, vbTab)  ' Returns zero-based array
      For jj = LBound(abc) To UBound(abc)
          VBFlexGridCells(jj, rw) = abc(jj)
      Next
  Next
End Sub

Running this will consume about 700 Mb of memory.

Now, if we move the entry assignment out of the loop:

ReDim VBFlexGridCells(0 To 9, 1 To 15000)

entry = aa & Chr(9) & bb & Chr(9) & cc & Chr(9) & dd & Chr(9) & ee & Chr(9) & ff & Chr(9) & kk & Chr(9) & ll & Chr(9) & mm & Chr(9) & nn

For rw = 1 To 15000
    Dim abc() As String
    abc = Split(entry, vbTab)  ' Returns zero-based array
    For jj = LBound(abc) To UBound(abc)
        VBFlexGridCells(jj, rw) = abc(jj)
    Next
Next

only about 320 Mb will be consumed, which is really weird.

So one option to get the normal memory consumption is to move the entry assignment out of the loop.

If that is not an option (because in the production code entry is different on each iteration), there is another approach: it would appear that copying the array also optimizes the memory it consumes:

    Dim temp_array(0 To 9, 1 To 15000) As String

    For rw = 1 To 15000
        entry = aa & vbTab & bb & vbTab & cc & vbTab & dd & vbTab & ee & vbTab & ff & vbTab & kk & vbTab & ll & vbTab & mm & vbTab & nn

        Dim abc() As String
        abc = Split(entry, vbTab)  ' Returns zero-based array
        For jj = LBound(abc) To UBound(abc)
            temp_array(jj, rw) = abc(jj)
        Next
    Next

    'Here we are consuming 700 Mb
    VBFlexGridCells = temp_array  ' Copying
    'Here we are consuming 1020 Mb
End Sub ' Here we are consuming 320 Mb

If you have enough memory to survive the moment where the "proper" and the "bloated" array exist at the same time, that may also be a solution.

I'm really lost why it happens otherwise.

GSerg
  • 76,472
  • 17
  • 159
  • 346
-2

Every string concatenation in VB will allocate a new string and then copy the data over and then de-allocate the original once it can.

For fast and memory optimised string concatenation use StringBuilder class. If you do a significant number of manipulations on a string, such as concatenations, deletions, and replacements, your performance might profit from the StringBuilder class in the System.Text namespace.

https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/operators-and-expressions/concatenation-operators

Abhishek
  • 5
  • 6
  • There is no `StringBuilder` in VB6, and unlike in VB.NET, strings in VB6 are mutable. – GSerg Jan 14 '19 at 08:21
  • I suggest using a string builder equivalent, such as the one mentioned here https://stackoverflow.com/questions/93932/out-of-string-space-in-visual-basic-6 or just search vb6 string builder in google – Abhishek Jan 14 '19 at 10:43