views:

1258

answers:

3

Is there such a thing?

I'm talking about something like a C++ new command i.e. allocation of memory which necessitates explicit releasing of the memory (or risk memory leaks).

I ask because I remember having to solve some GDI leak problems previously by setting forms/controls/other objects to Nothing but can't remember what or why now...

Do we ever have to worry about memory management when developing in VB6?

+1  A: 

I'd like to say you never have to worry about memory management, but it's not quite true. It depends to some extent on the execution environment that your VB6 code is running in. I have certainly seen VB6 classes running under COM+ that would leak memory if they didn't explicitly set object references to Nothing when finished with them.

Environmental issues aside, memory that you allocate within the VB6 type system will generally be cleaned up for you. I'm talking about things you allocate with the New keyword. But there is a significant exception, pointed out by rpetrich and others: -

Because of the reference-counting mechanism that VB uses to manage the lifetime of allocated objects, it is possible to leak memory if you have any cyclic references. For example, A->B->C->A. If you have that kind of scenario you'll probably need to spot it yourself and cure it by explicitly setting references to Nothing. I am not aware of any tools that help much with identifying this kind of problem.

Further problems come in when you're using libraries written in other languages. You might New-up a COM object written in C++ that allocates some memory internally, and find that you have to call a particular method (such as Close) to free that memory. Maybe such a COM object would be badly-written, but plenty of them exist.

So there are no rules to follow, except perhaps: -

  1. Try to know as much as possible about the behaviour of any libraries you use, and
  2. Always run your code while watching a memory trace in Performance Monitor to make sure its memory usage isn't growing in an unbounded fashion.
  3. Try to be aware of cyclic references ;-)
Martin
Since VB6 (and COM in general) uses reference counting, it is possible to leak objects via circular references. You should edit your second paragraph to reflect this
rpetrich
+2  A: 

Yes, I had simmilar problems with various forms, so I set them explicit to nothing on each unload.

But the problems where mostly with 3rd party controls, seems that sometimes not all COM references where cleared correctly.

Take a look here.

danimajo
+5  A: 

There are several areas of concern as far as Memory management in VB6.

The first are circular references where a child class points back to a parent and vice versa. Without explicitly setting the reference to Nothing, This is sometimes true of forms as well especially a dialog that is an editor for a Target object. Again making sure everything set to nothing will solve the problem.

The fundamental principles are 1) If anything pointed to by an object is "alive" then it won't be garbage collected. So when you set the reference to the parent object of a circular reference the child is alive so the parent doesn't get garbage collected, since the parent still alive the child doesn't get garbage collected.

The same with forms. If you don't set the Target Property of a dialog that editing an object to nothing than it won't fire the final series of events as long as the Target Object is alive.

The most common side effects of doing this are that your application won't shut down properly and your memory footprint will grow the longer the application is used.

As for GDI leaks, anytime you use an external DLL that uses handles, pointers. You put yourself in the same realm as C++ for those functions. So you have to make sure that you follow all the rules of the particular API or DLLs you are using which often involves explicitly destroying that which you created after you are done with it.

There is an elegant solution for the circular reference problem. Instead of the child referencing a parent directly you use a proxy.

First make a Proxy Class for the parent object.

Option Explicit Public Event GetRef(ByRef RHS As MyObject)

Public Function GetMyObject() As MyObject
    Dim Ref As MyObject
    RaiseEvent GetRef(Ref)
    Set GetMyObject = Ref
End Function

Then define a private variable in the Parent

Private WithEvents MyProxy As MyObjectProxy

Private Sub Class_Initialize()
    Set MyProxy = New MyObjectProxy
End Sub

Then setup a read only property called Proxy and implement the GetRef event.

Public Property Get Proxy() As MyObjectProxy
    Set Proxy = MyProxy
End Property

Private Sub MyProxy_GetRef(RHS As MyObject)
    Set RHS = Me
End Sub

For the child or anything else that needs a reference the code is as follows.

Private ParentProxy As MyObjectProxy

Public Property Get Parent() As MyObject
    If ParentProxy Is Nothing Then
        Set Parent = Nothing
    Else
        Set Parent = ParentProxy.GetRef
    End If
End Property

Public Property Set Parent(RHS As MyObject)
    If RHS Is Me Then
        Set MyObjectProxy = Nothing
    ElseIf Target Is Nothing Then
        Set MyObjectProxy = Nothing
    Else
        Set MyObjectProxy = RHS.Proxy
    End If
End Property

Because the event mechanism doesn't set references or increments the COM reference count on either object it avoids the whole circular reference problem that is the bane of many VB6 programmers.

Note: The source I got it from called it a Proxy but thanks to Anthony's comment I find it also fits the definition of the Mediator Pattern. It uses a specific VB6 Centric feature; the Event API which isn't quite in the spirit of the Mediator Pattern.

Also realize that the .NET framework has equivalents to VB6's Event API although it is implemented differently (delegates, etc)

RS Conley
This is known as the Mediator pattern, worth adding to the text of this answer.
AnthonyWJones