[AccessD] Framework Discussion - Troubleshooting

John W. Colby jwcolby at colbyconsulting.com
Fri Mar 5 06:10:37 CST 2004


When I started using classes one of the first problems I encountered is the
problem of tracking what classes loaded, and did they properly unload.
Loading a class and not unloading it when you are finished causes a memory
leak where the memory for the class is not returned to Windows.  Sometimes
Access will return the memory when Access closes, occasionally it can cause
Access not to close correctly and this causes not only memory leaks (HUGE)
but other problems as well.  These ghost Access instances cannot be seen in
Windows 98 and AFAIK there is no way to close them in versions of Windows
prior to A2K.  With A2K and above you have to use the task manager.  Either
way it is ugly.  It is therefore critical that we keep track of what is
loaded and ensure that we unload them when we are done.

In my current framework I use a long integer that each class directly
increments as it loads.  This was my first tool, taken from example code
from Shamil.

'*+ Module Variable declaration
Private mlngObjCounter As Long      'A COUNT OF ALL INSTANCES OF CLASSES IN
THE SYSTEM

Public Sub IncObjCounter()
    mlngObjCounter = mlngObjCounter + 1
end sub

Public Sub DecObjCounter(strObjName As String)
    mlngObjCounter = mlngObjCounter - 1
end sub


While this was useful and at least told me a gross count of what was loaded,
I decided I really wanted to be able to see the names of the class instances
loaded so I added a class.  In order to do this I added a collection into
which I would add the name of the class loading, and remove that class' name
as the class unloaded.

Private mlngObjCounter As Long      'A COUNT OF ALL INSTANCES OF CLASSES IN
THE SYSTEM
Public mcolObjNames As Collection

Public Sub IncObjCounter(strObjName As String)
    mlngObjCounter = mlngObjCounter + 1
    mcolObjNames.Add strObjName
end sub

Public Sub DecObjCounter(strObjName As String)
    mlngObjCounter = mlngObjCounter - 1
    mcolObjNames.Remove strObjName
end sub

I could then use a function to read out the strings in the collection:

Public Function ObjNames() As String
On Error GoTo Err_ObjNames
Dim strName As Variant
Dim str
    For Each strName In mcolObjNames
        If Len(str) > 0 Then
            str = str & "; " & vbCrLf & strName
        Else
            str = strName
        End If
    Next strName
    ObjNames = str
end function

This works reasonably well and is the system I have lived with for quite
awhile now.

For the new framework I decided to store a pointer to the actual class
instead of just the name of the class.  The class calls a function that
stores a pointer to itself in the collection as it loads, and a function
that removes its pointer as it unloads.  I do this because I now have a
single place to go to look at and manipulate if necessary any class
instance.  One of the problems we deal with is "where is the pointer to this
class stored"?  In other words, a class can be loaded in code in a form, and
the pointer stored in a variable in the form's header, or perhaps in a
function in some piece of code and just stored in a variable local to that
function, or perhaps in some class and stored in a collection in that class.
If you want to go look at the class, execute a method, examine a property
etc. how do you "get at" a pointer to the class?  By storing a pointer to
every class loaded in a collection we can now:

a. get a count of all classes loaded by examining the collection count
b. get a list of the names by iterating the collection asking each class
it's name and building up a string
c. get at the actual class and manipulate it if needed by getting the
pointer from this class.

For this reason I have decided to use this method.

disclaimer: it is important to realize that classes do not truly unload from
memory until the last pointer to the class is set to nothing.  Thus if you
instantiate a class, store a pointer in this collection and a variable in a
form header (for example) the class will not unload until you delete the
pointer in the collection AND in the form.  If your code stores a pointer in
another variable somewhere, the class will not close until ALL THREE
pointers are set to nothing.  My class init() / term() methods save the
pointers in this collection as well as a child collection of the classes
parent object (more on that later), and as the class unloads it will clean
out these two instances.  However if YOUR code saves a pointer in some other
variable, I don't know that and can't clean that up, so the class will
remain open until YOU set your pointer to nothing.

I am toying with using a collection in each class that is a pointer to all
the variables holding pointers to that class.  I.e. if you need to store a
pointer to the class in a variable of your own (perhaps to sink events?)
then you call a method of the class passing in the variable you will use.
The class puts a pointer to itself in that variable AND ... saves a pointer
to that variable in a collection inside the class.  Now the class CAN clean
up all those errant pointers to itself by simply iterating it's own
PointersToSelf collection setting each variable to nothing and removing the
variable from its collection.

Well, gotta go earn money now.  Comments welcomed.

John W. Colby
www.ColbyConsulting.com





More information about the AccessD mailing list