ByVal and ByRef and the Garbage Collection (strange behavior)

Robert_Zenz

Well-known member
Joined
Jun 3, 2008
Messages
503
Location
Vienna, Austria
Programming Experience
3-5
Hello.

Since my Project has reached some size I've started using the Garbage Collection and trying to find best ways to keep the useage of memory and cpu small. However, I was trying out how ByVal and ByRef are behaving with the garbage collection and found some pretty strange behavior (code below).

I've created a test application with two checkboxes and a timer. The timer refreshes a label with System.GC.GetTotlaMemory(False). If the checkboxes get checked, a function is called which receives a String (filled with 2^25 spaces) (ByRef or ByVal) and circles while the checkbox stayes checked (to see the actual memory useage).

If I check the ByVal Checkbox2 the memory usage grows up on 67MB and stays there until I uncheck it, then it falls back on some hundred kilobytes.
If I check the ByRef Checkbox1 the memory useage also grows on 67MB, but stays there even after unchecking! To me this seems like that the ByRef Object does not get destroyed by the garbage collection, but ByRef shoudln't create any object at all. I would expect such an behavior from ByVal, but not from ByRef since there is no actual copy of the object. Can somebody explain this to me?

If you want to try to reproduce it, just copy this Code onto a form with a Timer, a Label and two checkboxes:

Code:
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Me.Label1.Text = System.GC.GetTotalMemory(False).ToString("N0")
    End Sub

    Private Sub testRef(ByRef sender As Object)

        While CheckBox1.Checked

            Application.DoEvents()
        End While
    End Sub
    Public Sub testVal(ByVal sender As Object)

        While CheckBox2.Checked

            Application.DoEvents()
        End While
    End Sub

    Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
        Dim str As String = Space(2 ^ 25)

        Me.testRef(str)

        str = Nothing

        System.GC.Collect()
    End Sub
    Private Sub CheckBox2_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox2.CheckedChanged
        Dim str As String = Space(2 ^ 25)

        Me.testVal(str)

        str = Nothing

        System.GC.Collect()
    End Sub
Best Regards,
Bobby
 

vis781

Well-known member
Joined
Aug 30, 2005
Messages
2,015
Location
Cambridge, UK
Programming Experience
5-10
The memory is being released in both situations. It's just that your example does not show it. The only difference between the Byref and Byval is that there are few extra op codes in the call from the CheckedChanged event to the ByRef routine IL. To make this a fair test the line below should be changed from..

Code:
 Me.Label1.Text = System.GC.GetTotalMemory(False).ToString("N0")
to

Code:
 Me.Label1.Text = System.GC.GetTotalMemory(True).ToString("N0")
To ensure that the the garbage collector has performed a cycle before returning.
 

Robert_Zenz

Well-known member
Joined
Jun 3, 2008
Messages
503
Location
Vienna, Austria
Programming Experience
3-5
Hello.

Yes, indeed.
But still I wonder, I call at the end of each event System.GC.Collect(), shouldn't that do the same thing as GetTotalMemory(True)? Or is there something I'm missing?

Bobby
 

vis781

Well-known member
Joined
Aug 30, 2005
Messages
2,015
Location
Cambridge, UK
Programming Experience
5-10
No, GC.Collect will start a garbage collection but unless the Parameter in GetTotalMemory is True that routine will not wait for the GC to finish it's cycle. So it seems there is some difference with regards to how long it takes for the GC to finalize and destroy objects based on whether they are passed byref or byval perhaps some additional safety checking to ensure no more valid pointers point to that object. Perhaps some additional work is carried out in compacting and optimizing the heap. Either way the result is the same in that GetTotalMemory will return with the current amount of memory that is thought to be used by the application but it doesn't mean for definate that the GC will have finished collecting buy the time the rountine returns. Unless you have the parameter set to true.
 

cjard

Well-known member
Joined
Apr 25, 2006
Messages
7,052
Programming Experience
10+
Hello.

Since my Project has reached some size I've started using the Garbage Collection and trying to find best ways to keep the useage of memory ... small.
Please don't do this. I can guarantee that you will achieve nothing more than harm the performance of your app for no gain. The garbage collector knows when to run, and you shouldnt concern yourself with apparently large numbers that you see in task manager; they are of little real world consequence and windows memory management doesnt need your efforts at micromanagement in order to function.
 
Top Bottom