Wiki Home

Manual Garbage Collection


Namespace: VFP

A great way to totally eliminate C5 errors is do your own garbage collection. I push VFP as hard and as convolutedly as anyone and I don't ever C5 on NT. Never.-- Steven Black
Does anyone know whether the memory is really released instantly when an object's release() method is called? Or does VFP cache it for some time before the memory is released? -- Nancy Folsom

Nancy - if the method is called from inside the object itself this.Release() it won't release until all the object methods are off the call stack and there are no other references to the object. VFP does not use a seperate garbage collection thread like Java. Do you see some particular behavior that doesn't make sense? -- ?df

David- No, it's spending time reading about Garbage Collection in ref. to .NET. I've learned more about GC than I ever thought I wanted to know. And now I want to know how it works in VFP. >g< Specifically, an example is if you create a connection object and the object goes out of scope. Will the connection be closed? Yeah, I know I can test it...the example was just moments ago pointed out to me. -- Nancy Folsom

Nancy - you mean like a SQL connection? No If SQLDisconnect() is not called you'll have a dangling connection. If the handle is stored as an object property the object should make the call in it's Destroy() to free the connection. Personally, I don't like the free wheeling garbage collection of Java, but I think it's better than the memory leaks prevalent in bad C++ code. VFP is better than both in this regard IMHO. In any language though why would one not write code that was careful about cleaning up after itself? -- ?df

This page is great if you're writing new code and want to avoid dangling references. But how does one go about tracking down a dangling reference in bought/borrowed/inherited code? Preferably a little faster than a line by line code review. See Debugging Visual FoxPro Applications perhaps? I'd start by ensuring that all the obvious classes that contain, refer, or control other instances, all GodObjects or ManagerObjects for example, clean-up explicitly after themselves. -- Steven Black

I think this is a place where a nice side-benefit of the Abstract Factory pattern can shine. If you are avoiding raw CREATEOBJECT() calls and instead get your new objects from a factory object, then you have a HookPoint at which you can:
1) Log the creation of an object
2) BINDEVENTS() to the destroy() method of the new object so as to fire a method to log the destruction of the object whenever it happens.
The nice thing about this approach is that you can adopt it without wiring any monitoring code/information into your classes.
As for bought/borrowed/inherited code, you can wire in a factory by searching for CREATEOBJECT() and replacing it with a suitable reference to your factory object. - Lauren Clarke
Steven - could you share with us what you mean by doing your own garbage collection? -- Susan Giddings

Assuming object has properties that point all over the place, to other sibling objects and even objects nested within other objects, instead of RELEASE oObject or letting oObject go out of scope, explicitly tell the object to release itself, like this:
oObject.Release()

Now within oObject you have the following methods:
PROCEDURE Release
  *-- This is base class behavior.
  This.GarbageCollect()
  Release This

PROCEDURE GarbageCollect
  *-- This is base class behavior.
  *-- If this object is some sort of container
  FOR EACH oControl IN This.Controls
    IF PEMSTATUS(oControl, "GarbageCollect", 5)
      oControl.GarbageCollect()
    ENDIF
  ENDFOR

  *-- Now this is instance behavior so
  *-- Your method starts mere with DoDefault()
  *-- Now clean up situational artifacts at this level
  This.oPointer1 = .NULL.
  This.oPointer2 = .NULL.

PROCEDURE DESTROY
  This.GarbageCollect() && Only to be sure it gets called
  DoDefault()

The other thing to keep in mind is one of the Object Oriented Heuristics that says Objects at the same level of lexical scope should not have a USES relationship between them. In other words, sibling objects should not have properties that point to each other because, in some cases, VFP is unable to clean up the references when destroying objects. VFP 6 is much better than VFP 5 and VFP 3 at cleaning up pointers, but in convoluted situations you'll see C5 errors unless you clean up these pointers yourself.-- Steven Black
Steve: Great post (and nice job formatting the VFP code :>)! One idea I had is that you penalize yourself for calling Release() since the GarbageCollect method gets called twice (again by destroy). Since this may be slow, with all of its PEMSTATUS calls, what about a logical property "lGarbageHasBeenCollected" that gets set by GarbageCollect so that you can refer to it and avoid duplicating the task. Not sure if this is good or bad. -- Randy Pearson

The FoxPro Foundation Classes (FFC) use this idea with an lRelease property. It's normally .F. but is set to .T. during a release operation. Destroy only does its garbage collection if lRelease is .F. -- Doug Hennig

But... what object is responsible for calling release? What if you have an object which is used by several other objects... you want the object to release when the last object referenceing it is destroyed... this method seems to preclude that. Are you say that having GarbageCollect() in destroy and issueing an This.oObject = null is not adequate? Thanks.

My rule of thumb is this: if Object1 creates Object2, it'll call Object2.Release(). If not, it sets its reference to Object2 to .NULL. since someone else created the object and may still need it -- Doug Hennig
I have been doing C++ development recently, specifically COM development. An interesting aspect of COM objects is that they keep a count of the number of references to themselves. The same approach can be used within VFP to determine when an object needs to release itself. Using this methodology, if Obj1 creates or references Obj2, it should call Obj2.Release(). Here is an example to illustrate this:
*--- Class2
PROCEDURE AddRef
nReferrers = nReferrers + 1
RETURN

PROCEDURE Release
nReferrers = nReferrers - 1
if nReferrers < 1
  *-- call GarbageCollect method
  this.GarbageCollect()
else
  NODEFAULT()
endif
RETURN

*--- Class1
PROCEDURE Init
  oObj2 = AddObject("Class2")
  oObj2.AddRef();
RETURN

PROCEDURE Release
  oObj2.Release()
  oObj2 = .NULL.
RETURN

This allows you to treat the referenced object's cleanup identically regardless of the creator. -- Brian Marquis

- So now, whenever I create another reference to a class I have to be sure to call AddRef. There must be some internal reference counter, if only there were a way to access it. -- Bob Archer
---
Hmm.... Wouldn't it be a lot simpler to do something like this, instead of using 2 lines each time?:
Not that I'm saying this would be the best solution, I just thought that this should be pointed out, since we can't access the internal referance counter:
-- Peter Crabtree
*--- Class2
nReferrers = 1 && There has to be ONE referance to be created, doesn't there?
PROCEDURE GetRef
nReferrers = nReferrers + 1
RETURN THIS

PROCEDURE Release
this.nReferrers = this.nReferrers - 1
if this.nReferrers < 1
  *-- call GarbageCollect method
  this.GarbageCollect()
else
  NODEFAULT
endif
RETURN

*--- Class1
PROCEDURE Init
  oObj2 = AddObject("Class2")
RETURN

PROCEDURE Release
  oObj2.Release()
  oObj2 = .NULL.
RETURN

 *--- Class 3
PROCEDURE Init
  this.oObj2 = oObj2.GetRef()



I think this method will be generalized for uses relationship also.
For example, to release forms referencing each other, use the following code.

FUNCTION ReleaseAllForms

FOR lnFmIdx = _SCREEN.FormCount to 1 STEP -1
  IF type('_SCREEN.Forms[lnFmIdx].BaseClass')!='C' or ;
            upper(_SCREEN.Forms[lnFmIdx].BaseClass) != 'FORM'
    LOOP
    ENDIF
  lForm = _SCREEN.Forms[lnFmIdx]
  IF !lForm.queryunload()
    return .f.
    endif
  IF type('lForm.name')!='C'
    loop
    endif
  lForm.Release()
  ENDFOR

FOR lnFmIdx = _SCREEN.FormCount to 1 STEP -1
    if type('_SCREEN.Forms[lnFmIdx].BaseClass')!='C' or ;
	            upper(_SCREEN.Forms[lnFmIdx].BaseClass) != 'FORM'
      loop
      endif
  _SCREEN.Forms[lnFmIdx].Release()
  ENDFOR

* Release() raises form controls destroy events, eat them.
PRIVATE p_notreleased
p_notreleased = .F.
_screen.addobject( 'ExitTimer', 'tmrExit' )
* This should be replaced with waiting loop:
=INKEY( 2 )
RETURN !p_notreleased

DEFINE CLASS ExitTimer AS TIMER
interval = 1500

PROCEDURE INIT
this.enabled = .t.
ENDPROC

PROCEDURE Timer
this.enabled = .f.
* Check for close
FOR each oForm in _screen.Forms
  IF type('oForm.BaseClass')!='C' or upper(oForm.BaseClass) != 'FORM'
     loop
     endif
  * Dangling reference
  p_notreleased = .T.
  RELEASE THIS
  RETURN
  ENDFOR
RELEASE THIS
ENDPROC
ENDDEFINE

You need to call this code before packing, reindexing, re-login etc. commands. Andrus Moor

Are you saying that any time you want to pack or reindex you must release all forms in an app? -- Rick Hodder

Yes. Forms are bound to tables. To pack them you need to use them exclusive. Normal way is to close all forms before packing tables, isn't it ? -- Andrus Moor

Been pondering on this a bit.

Seems to me the clean way of handling object references to ensure they are always properly removed when the referring object destroyed is to give the job of keeping track of references to the object being referred to. This could take place in add_reference() and remove_reference() methods of each object.

If object_a wanted to create a reference to object_b, object_a would probably pass the request for a reference to an application mediator which would process the request and return the object_b reference if object_b was in fact instantiated and visible, such as…
Object_a.object_b_reference = oApp.GetObjectReference( “object_b” )
The application .getObjectReference() code would be something like the following:

* oApp.GetObjectReference()
*
LPARAMETERS tcObjectName

LOCAL lcOldOnError, llError, loObjectReference

* Save the current error handler
lcOldOnError = ON( “ERROR” )

* If an error occurs, merely set llError to true
ON ERROR llError = .T.

loObjectReference = EVALUATE( tcObjectname )

* If llError is true, lcObjectname did not evaluate.  If
* lcObjectName did evaluate, make certain it evaluated to
* An object.

IF llError .OR. VARTYPE( loObjectReference ) # “O”
    shouldn't it be: VARTYPE( loObjectReference ) # “O”
   Yes it should.  Thanks, JME (Changed 03/28/01)
   * If an object reference cannot be returned, return .NULL.
   loObjectReference = .NULL.
ELSE
   * See if loObjectReference will allow itself to be
   * referenced.  If so, the addReference() method will
   * increment the object’s reference counter and return
   * a reference to itself.  If not, addReference() will
   * return .NULL.
   loObjectReference = loObjectReference.addReference()
ENDIF

* Restore the former error handler
IF EMPTY( lcOldOnError )
   ON ERROR
ELSE
   ON ERROR &lcOldOnError
ENDIF

RETURN loObjectReference

* End of method GetObjectReference()
The addReference() method of the referenced object would contain the following default code.
* AddReference()
*
* Determines whether an external reference to this object is
* Permitted.  If so, increments the reference count and returns
* A reference to this object.  If not, returns .NULL.
*
* Properties:
*   .allowExternalReference	(L) Specifies whether an external
*	                            reference to this object may be created.
*   .referenceCount		(N) Specifies the number of external
*	                            references to this object that have been created.
*
WITH this

   IF .allowExternalReference
      .referenceCount = .referenceCount + 1
      RETURN this
   ELSE
      RETURN .NULL.
   ENDIF

ENDWITH

* End of method addReference()
Getting rid of the reference is much easier because the services of a global mediator are not needed, just call the .removeReference() method of the referenced object.

However, one more complexity arises in determining whether an object should be destroyed when the last external reference to the object is removed. Generally, we do not want an object destroyed if another object is using it. So no referenced object would be destroyed until its .referenceCount reached zero. But even then we may not want to destroy the object. For example, object_a on form_A may contain a reference to object_b on form_B. If object_a releases its reference to object_b, we do not want to destroy object_b if form_B still has a use for it even though object_b’s referenceCount is now zero.

Therefore, we need another property, .destroyOnExternalRelease which specifies whether the object should be destroyed when the its last external reference is removed. Its default would be false, which would prevent it from being destroyed.
WITH Object_a
    .object_b_reference =.object_b_reference.removeReference()
ENDWITH
The removeReference() method of each object would contain the following default code.
* RemoveReference()
*
* Properties:
*   .referenceCount           (N) Specifies the number of external
*                                 references to this object that have been created.
*
*   .destroyOnExternalRelease (L) specifies whether the object
*                                 should be destroyed when the its last external reference
*                                 is removed.
*
WITH this

   .referenceCount = .referenceCount + 1
	
   IF .referenceCount < 1
      IF PEMSTATUS( this, “release”, 5 )
         .release()
      ELSE
         .parent.removeobject( .name )
      ENDIF
   ENDIF

   RETURN .NULL.

ENDWITH

* End of method removeReference()

-- Jim Edgar

With Endwith
I have discovered that WITH holds an object reference. The object does not release until the ENDWITH. At that point the destroy and unload methods fire (probably among others).
WATCH OUT! If you release an object inside a With Endwith, and expect it to be gone, then call or execute code that won't work right if the object is not gone, you will have a problem. I encountered this when I called a legacy DOS subroutine that does an @ SAY to the underlying screen. The screen was not the WOUTPUT! Even though I had released the form class associated with the WITH, it was still there. It released properly at the ENDWITH.
Ray Kirk
Yet Another Garbage Collector -- This one is smaller in scope and similar to Steve's example above. Since I don't use containers for my business objects, I wanted a routine that would go through all of the properties of the object and release any references to other objects. I leave the reference counting and object destruction to VFP, assuming (perhaps incorrectly) that once all the references are gone, VFP will do its job. Anyway, probably not the fastest piece of code, but you might get some use out of it.

* CleanUp() method
Local Array laProperties[1]
Local lcPropertyName, lcProperty

* If clean up already run, return
* Assumes class has lCleanup property defaulted to .T.
If !This.lCleanUp
	Return
Else
	* Don't run again. Also prevents infinite loop caused by circular references.
	This.lCleanUp = .f.
EndIf

AMembers(laProperties, This)
For each lcPropertyName in laProperties
	lcProperty = "This." + lcPropertyName
	* Determine if property is object
	If Type(lcProperty) = "O" and !IsNull(&lcProperty)
		* Run CleanUp() on member object before releasing
		If PemStatus(&lcProperty, "CleanUp", 5)
			&lcProperty..CleanUp()
		EndIf
		* Release reference
		&lcProperty = .f.
	EndIf
EndFor

-- Joel Leach
Although I'm not certain it's required, it seems like it might be prudent to call: UNBINDEVENTS(THIS) at some point during the deconstruction process in VFP8. - Lauren Clarke
Am I correct in thinking that using ADDOBJECT() rather than Create Object() will remove the need for manual garbage collection ? This obviously means that you need to use containers for business objects etc but takes care of manual plumbing.
This may be the obvious method that everyone uses - I'm probably behind the times :) I've been using bo's based on the relation class for too long I think.
Jamie Osborn

All I see in the above code is objects manualy created being manually released. Is'nt that what the form in question will do when it is "Destroyed"? Provided there are no dangling references to objects in the form, the form will close and will it not then release all its objects?

I have noticed that a VFP exe when first run uses a certain amount of memory. In my case 12 megs. But if I minimize it the usage goes down to 1024 bytes and if I then maximize it again the usage goes up to 4 to 6 meg. To my understanding that is where the garbage collection takes place.

In .NET you can call Manual GC while the form is still open and you will immediately see a drop in usage. Can this be done in VFP? Surely if you release the objects in a form while the form is open, it will break the form. Can GC be done with a form still open to simulate the Minimize/Maximize of the VFP window?

This has *nothing* to do with runtime memory requirements, and the measure thereof. This article is about how to help VFP's automatic garbage collection by manually releasing your user-defined inter-object references. See in VFP we can have both immutable object ownership (containership) and mutable owhership (referencing). Problem is, depending on the nesting of references, VFP can get confused. This is especially bad in prior and older versions of VFP. For more, See Ambiguous Or Contentious Object Ownership. -- Steven Black
Category Class Design Category 3 Star Topics Category Code Samples
( Topic last updated: 2004.09.23 09:52:07 AM )