When you run a procedure with only static objects, these objects come and go with the procedure that defines them. Because the AVM can see the individual DEFINE statements for the objects, it knows when to create them, when they go out of scope, and when to delete them. You do not have to worry about deleting them because The AVM does it automatically for you.

The same is true, incidentally, of handles for static objects. When you store the handle of a static object in a HANDLE variable, that value is just a pointer to the object. It does not extend or affect the scope of the static object in any way. The variable itself, because it is defined statically, is also deleted by the AVM when its procedure goes out of scope.

When you create dynamic objects, however, the AVM cannot control their scope or their lifetime. If you create a dynamic object inside a loop, ABL might have no way of knowing at compile time how many of those objects the procedure will create. If you pass the handle of an object as a parameter to another procedure, ABL has no way of knowing at compile time where the handle came from, what it will be used for, or when the object it represents can safely be deleted. This is why you are responsible for taking care of this.

To see a dramatic example of the importance of memory management:

  1. Open the h-DeleteObject.p procedure used earlier and comment out the statements that delete the dynamic query and buffer objects at the end of the loop:
    /* h-DeleteObject.p */
    DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
    DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
    DEFINE VARIABLE iCount AS INTEGER NO-UNDO.
    
    ETIME(TRUE).
    DO iCount = 1 TO 100:
      FOR EACH _file WHERE _file-num > 0 AND _file-num < 32000:
        CREATE QUERY hQuery.
        CREATE BUFFER hBuffer FOR TABLE _file._file-name.
        hQuery:SET-BUFFERS(hBuffer).
        hQuery:QUERY-PREPARE("FOR EACH " + _file._file-name).
        hQuery:QUERY-OPEN().
        hQuery:GET-FIRST().
        hQuery:QUERY-CLOSE().
        // DELETE OBJECT hQuery.
        // DELETE OBJECT hBuffer.
      END.
    END.
    MESSAGE "100 iterations took " ETIME "milliseconds" VIEW-AS ALERT-BOX.
  2. Bring up the Windows Task Manager, select the Details tab, and find the running executable called prowin.exe:
  3. Run the procedure, and watch the memory go:
    After just 1000 iterations of this loop, which only created two dynamic objects, you have lost 839 megabytes of memory! Not only that, but even though you removed two key statements from the procedure, it ran slower:

The reason for this is that all that extra memory allocation is getting in the way of your procedure running efficiently. So you pay a heavy price all the way around.

Use the DELETE OBJECT statement to get rid of the memory for any dynamic object, regardless of its type. To summarize, you use a CREATE QUERY statement to create a dynamic query, a CREATE BUTTON statement to create a dynamic button, and a CREATE BROWSE statement to create a dynamic browse, but you use the DELETE OBJECT statement to clean up each of them.

You must also remember to clean up persistent procedures when you are done with them. Every time you execute a statement of the RUN procedure-name PERSISTENT SET proc-handle form, you are also allocating memory for the procedure and all its contents. You need to delete the procedure, when you are done with it, with the DELETE PROCEDURE statement.