You must clean up persistent procedure instances you have created just as with other objects. Remember that the RUN prog.p PERSISTENT statement is much the same as CREATE object. The AVM creates a running instance of the procedure and all its contents, and leaves it in memory until you specifically delete it. The simplest way to delete a procedure when you’re done with it is to use this statement:
DELETE PROCEDURE procedure-handle.

Note that you use the PROCEDURE keyword in this statement, not OBJECT.

The AppBuilder templates observe a convention that is a very useful alternative to simply deleting a procedure. If you delete a procedure from the outside, it might not be finished doing what it needs to do and might not have a chance to clean up the objects inside it properly. It is much more reliable, and much more object-oriented, to tell the procedure to delete itself rather than to simply kill it.

The procedure’s main block runs a standard internal procedure called enable_UI and then waits for a CLOSE event if the procedure is not being run persistent. If it is being run persistent, then it stays in memory without the need for any WAIT-FOR statement:
/* Now enable the interface and wait for the exit condition. */
/* (NOTE: handle ERROR and END-KEY so cleanup code will always fire. */
MAIN-BLOCK:
DO ON ERROR UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK
    ON END-KEY UNDO MAIN-BLOCK, LEAVE MAIN-BLOCK:
  RUN enable_UI.
  IF NOT THIS-PROCEDURE:PERSISTENT THEN
    WAIT-FOR CLOSE OF THIS-PROCEDURE.
END.
The WINDOW-CLOSE event on the window applies the procedural CLOSE event to the procedure:
DO:
/* This event will close the window and terminate the procedure. */
  APPLY "CLOSE":U TO THIS-PROCEDURE.
  RETURN NO-APPLY.
END.
The main block also defines this CLOSE event, which runs the cleanup code in the disable_UI internal procedure, which again is part of the AppBuilder’s own standard mechanism for starting and stopping procedures:
/* The CLOSE event can be used from inside or outside the procedure to */
/* terminate it. */
ON CLOSE OF THIS-PROCEDURE
  RUN disable_UI.
And finally, disable_UI deletes any dynamic objects. Then, if the procedure was run persistent, it explicitly deletes itself. If it was not run persistent, then the AVM deletes it for you:
/* Delete the WINDOW we created */
  IF SESSION:DISPLAY-TYPE = "GUI":U AND VALID-HANDLE(C-Win)
    THEN DELETE WIDGET C-Win.
  IF THIS-PROCEDURE:PERSISTENT
    THEN DELETE PROCEDURE THIS-PROCEDURE.
END PROCEDURE.

Whether you use the AppBuilder templates or not, use some similar convention that makes sure your procedures clean up after themselves and deletes them when your application is done with them.