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)