Feedback
Did this article resolve your question/issue?

   

Article

Managing Memory within the ABL?

« Go Back

Information

 
TitleManaging Memory within the ABL?
URL NameP98993
Article Number000141606
EnvironmentProduct: Progress
Version: 8.x, 9.x
Product: OpenEdge
Version: 10.x, 11.x
OS: All supported platforms
Question/Problem Description
Managing Memory within the ABL?
Managing Memory within the 4GL?
How to find Memory leaks?
How to identify a Memory leakage?
Why care about memory management?
How and when problems of a memory leak happen?
Troubleshooting Memory leaks
How can a Memory Leak can occur?
 
Steps to Reproduce
Clarifying Information
Why care about memory management?

A common type of support case deals with (perceived) memory leak in some part of an application. Generally this occurs with dynamic programming without fully understanding the key principles.

Another common type of support case deals with perceived high memory usage - whether false positive or actual issue. Actual issues here can result in system-wide resource shortages and excessive swapping on the OS level. False positives need to be identified as such early; trying to fix these when there's no issue is counter-productive.

What is a memory leak ?

A memory leak, is where allocated memory is not freed although it is never used again. Repeated memory leaks cause the memory usage of a process to grow without bound, eventually causing performance issues and/or hard crashes.
  • In manual memory management, this usually occurs because objects become unreachable without being freed.
  • In tracing garbage collection, this happens when objects are reachable but not live.
  • In reference counting, this happens when objects are referenced but not live. (Such objects may or may not be reachable.)
How and when do these memory leak problems happen?

Memory leaks build up over time before becoming problematic at runtime, the real issues usually happen after deployment. To notice a memory leakage during development / QA stage typically requires a specific test plan.

Types of memory overview:

1. Static object memory:
In the context of classes and garbage collection, the term Static object memory is used to mean memory used to store static objects. By definition these static classes are loaded once for a session, and once allocated persist for the duration of the session.

2. Stack memory:
Stack allocation means run-time allocation and de-allocation of storage in last-in/first-out order. In ABL, the programmer does not get control over the stack, that is managed only by the AVM. Application code however, can trigger the stack to be pushed over the limit.

Fixed-size memory pool, provided by the Stack Size (-s) Client Startup parameter is used:
  • To hold temporary copies of data on their way from the DB or to the screen
  • During computation of expressions
Stack overflow errors are most common when data definitions are loaded for very large tables, when recursive procedures are involved, or when large amounts of data are being passed as parameters. Increase the stack size if one of the following messages appears:
SYSTEM ERROR: stkpush: stack overflow. Increase -s parameter.
SYSTEM ERROR: stkditem: stack overflow. Increase -s parameter
.

3. Widget Pools:
Widget Pools are used to manage traditional dynamic objects, they are not used for ABL Class instances
  • Unnamed and named pools
  • Unnamed are scoped to the procedure in which they are defined
  • Named are scoped to the session
  • Allocated as coded by the programmer
4. External Memory:
  • ActiveX, COM-HANDLEs and DLLs
5. Memory Pointers:
MEMPTR variables in ABL are the equivalent of byte arrays for most purposes.
However, when interacting with DLLs / Shared Libraries they also are used as actual pointers to memory allocated by the external library.

Types of Objects overview

1. Static ABL Objects:
  • A static object is statically defined in the code.
  • Static objects are bound at compile time and scoped to the instantiating program. Memory is allocated when the program starts executing, and freed when the procedure.p terminates.
  • As example, this code will implicitly define 3 static objects: a frame and 2 fill-ins to display the 2 fields:
FOR EACH CUSTOMER:
   DISPLAY CUSTOMER.CUSTNUM CUSTOMER.NAME.
END.

2. Dynamic Objects:
  • Dynamic Objects are created and bound at run time. The compiler does not know that they exist.
  • Heap allocation or dynamic allocation means run-time allocation and de-allocation of storage in arbitrary order.
  • Dynamic allocation is usually for objects whose size, quantity, or lifetime, could not be determined at compile-time.  
  • Memory is only allocated when the CREATE.... SET or RUN...... ASYNC statement executes.  This memory is not garbage collected automatically.  
  • To avoid problems of leakage with dynamic objects use DELETE OBJECT, or widget pools.
DEFINE VARIABLE hQry AS HANDLE NO-UNDO.
CREATE QUERY hQry.
hQry:SET-BUFFERS(HBUFFER).
hQry:QUERY-PREPARE(...).
hQry:QUERY-OPEN().
hQry:GET-FIRST().
REPEAT WHILE NOT hQry:QUERY-OFF-END:
  hQry:GET-NEXT().
END.
hQry:QUERY-CLOSE().
DELETE OBJECT hQry.

3. Handles to Objects:
  • ABL Handles are a type of variable used for referencing internal object types, they are not objects themselves.
  • A handle can go out of scope while the object it points to remains, or an object can be removed from memory while there are still handles referring to it.
4. Garbage collection (GC):
  • Garbage collection is also known as automatic memory management, which is the automatic clean up of unused objects, and freeing memory.
  • Garbage collection is performed by a garbage collector which recycles memory that it can prove will never be used again.
  • AVM implements a Garbage Collector for ABL Classes.
5. ActiveX Controls:
  • A COM object property is a value that defines visible, functional, and other characteristics of a COM object (ActiveX Automation object or ActiveX control).  
  • An ActiveX control's property is classified as a design-time or run-time property depending on when you can change it.
    • A design-time property can be changed using the Properties Window of the AppBuilder.  
    • A run-time property can be changed from the ABL at run time. 
  • Generally, both design-time and run-time properties can be read at run time.
  • In all other respects, COM object properties are functionally analogous to widget attributes.
  • A COM object method is a specialized function associated with a COM object that performs an action on the COM object or alters the behavior of the COM object. COM object methods may or may not return a value and may or may not require parameters.  A return value may be a component handle to another COM object; however, many methods return other types of information or no information at all.  Like widget methods, COM object methods can executed by direct invocation, as statements, rather than by invocation as part of an expression.
  • In all other respects, COM object methods are functionally analogous to widget methods.  The basic syntax for referencing COM object properties and methods from  the ABL is similar to that of referencing widget attribute and method references.  
The main differences include:
  • COM object property and method references can be chained to turn component handles into a single reference.  Thus, a COM-handle property value may be used to invoke a method of the referenced object, and a method return value can be used to reference a property. 
  • The parameters of some COM object methods might have to be supplied with more type information, depending upon the methods and how the COM objects are implemented.
  • All COM objects are dynamic objects, so there is never a need to qualify a COM object reference using a static container reference (such as one might do with a static FRAME or MENU widget).  
  • The portion of memory a COM object uses is allocated outside the scope of the ABL.  It is only accessible by COM-HANDLE variables.
  • Any time a COM object reference is stored in a COM-HANDLE variable it must be release using the RELEASE OBJECT statement.
  • The AppBuilder, operating in Design Mode, provides facilities for setting design-time properties for ActiveX controls + control-frame containers.
  • To access an ActiveX control that has been loaded into a session at run-time, use the control-frame COM-HANDLE attribute to get a handle to the control-frame COM object.  To return a handle to the control, use the design-time name of the ActiveX control as a property of the control-frame COM object.
Example: The following procedure fragment shows a control named hc_CmdButton being loaded into a control-frame and the handle to the control (controlHdl) is obtained using the control name (hc_CmdButton) property.  Later it releases the control and deletes the parent control-frame widget. (CFWidHdl). 
DEFINE VARIABLE CFWidHdl AS WIDGET-HANDLE.
DEFINE VARIABLE CFComHdl AS COM-HANDLE.
DEFINE VARIABLE controlHdl AS COM-HANDLE.
       
/* Create frame foo ... */
CREATE CONTROL-FRAME CFWidHdl
    ASSIGN FRAME    = FRAME foo:HANDLE
           NAME     = "ctlFrame1".
           CFComHdl = CFWidHdl:COM-HANDLE.

CFComHdl:LoadControls(hc_CmdButton.wrx,"hc_CmdButton").
controlHdl = CFComHdl:hc_CmdButton.
controlHdl:BgColor = RGB-VALUE(0,128,0).
       
/* do some more stuff ... WAIT-FOR ... */
RELEASE OBJECT controlHdl.
DELETE WIDGET CFWidHdl.
 
6. DLLs:

A shared library is a file that contains a collection of compiled functions (routines) that can be accessed by applications.  Such a file is called a shared object or shared library (.so) on UNIX and a Dynamic Link Library (.DLL) on Windows.

An application links to these routines at run-time rather than at compile/build-time, and shares the functionality of the code with other applications that can link to them. Shared libraries promote:
  • code re-use, because an application can reference third-party logic) and
  • upgradeability, because any enhancement to a shared library becomes immediately available to an application, without rebuilding it.
The ABL enables linking to and executing shared library routines by defining an "EXTERNAL" procedure, using much the same syntax as a standard "INTERNAL" procedure:

Example:
PROCEDURE GetWindowRect EXTERNAL "USER32.DLL":
  DEFINE INPUT PARAMETER hWnd AS LONG.
  DEFINE OUTPUT PARAMETER lpRect AS MEMPTR.
END PROCEDURE.

7. Persistent Option:

The ABL supports the PERSISTENT option on the entry point definition. It is no longer necessary to call LoadLibrary to ensure that a DLL remains in memory until the process is exiting, or FreeLibrary is explicitly called.
 
8. SET-POINTER-VALUE Function:
  • The ABL supports a SET-POINTER-VALUE function that allows setting the value of a MEMPTR variable.
  • SET-POINTER-VALUE is used to map a MEMPTR to memory allocated by a Windows Dynamic Link Library (DLLs) or UNIX shared library routine called from the ABL.
Example: The following code example calls a DLL routine that returns a pointer to a structure, extracts an address at byte 5 of the structure, uses SET-POINTER-VALUE to assign the address to a Progress MEMPTR, and displays the character string at the address.
DEFINE VARIABLE person_struct AS MEMPTR. /*pointer to structure */
DEFINE VARIABLE name          AS MEMPTR. /* pointer to name */

SET-SIZE(person_struct) = 8.
PROCEDURE person_info EXTERNAL "person.dll" PERSISTENT:
  DEFINE OUTPUT PARAMETER person_struct AS MEMPTR.
END PROCEDURE.

RUN person_info(OUTPUT person_struct).

SET-POINTER-VALUE(name) = GET-LONG(person_struct,5).
DISPLAY GET-STRING(name,1) FORMAT "x(50)".
SET-SIZE(person_struct)=0.
Error Message
Defect/Enhancement Number
Cause
Resolution
How to monitor memory usage:

For further information on monitoring memory usage, refer to the following Articles: ABL Memory Pointers:

For further information on Memory Pointers, refer to the following Articles: ActiveX/OCX objects:

For further information on ActiveX/OCX objects, refer to the following Article: Handles and memory management:

In memory management, a handle is a reference that represents another object. Handles are used to keep a fixed reference to an object, while allowing it to be moved in memory, or even swapped out to disk.  The program therefore cannot know the address of the object. The AVM re-uses memory, therefore a new object could be created using a handle to some object that was already deleted.  If a handle variable lives longer than objects created in a program, set it to unknown when the object is deleted. To check if a handle is valid, use VALID-HANDLE function.

Starting from Progress 9.1C, Progress AVM uses opaque handles. Rather than being more direct pointers allocated by the OS memory, a hash table is used to abstract the memory handling from the ABL programmer. Handles are assigned sequentially and are much less likely to be reused.

How to manage dynamic objects:

There are a key points and guidelines to consider and follow, in order to avoid leaking memory by leaking dynamic objects. The longer the interaction, the more time for a memory leak. Keep the scope of an object limited:

Examples:
  • CREATE OBJECT.
  • Do something with the object.
  • DELETE OBJECT.
  • Make sure a procedure deletes all objects that belong to it.
Or use WIDGET-POOLS to control scope of sets of dynamic objects, for further detail refer to Article: 000016756, Widget-Pool Explained .

Using persistent procedures:

An object is created when a procedure is run persistently using RUN .... PERSISTENT SET hProc. As the name implies, these persist in memory after the main block has terminated. This procedure must be deleted later using DELETE OBJECT statement. 
  • When a persistent procedure is leaked, any static object it defines is leaked along with it.
  • Avoid spawning multiple instances of a procedure at once where possible. Mostly: if procedure holds only functions, not data that must be persisted.
  • Good practice is to design procedures to either hold data / a specific state or to hold the logic that acts on the state, but not both. This allows procedures that need multiple instances to stay small.
  • Clean up procedures that aren't used anymore. Since procedure-based ABL does not support a Destructor-type construct, common practice is to set up a trigger in the persistent procedure to allow other cleanup code to execute. 
Example using CLOSE event:
ON CLOSE OF THIS-PROCEDURE DO:
  RUN SHUTDOWN.
  IF RETURN-VALUE <> "" THEN RETURN NO-APPLY.
END.
.....
PROCEDURE SHUTDOWN:
  /* Cleanup code here */
  DELETE OBJECT THIS-PROCEDURE.
END PROCEDURE.

Be aware of implicitly created objects:

Some objects are automatically created by the AVM but do need to be manually deleted. Typically, this is where an ABL statement implicitly generates an object that the ABL knows about but it is not clear to the programmer. This applies to:
  • Dynamic TEMP-TABLEs created by using TABLE-HANDLE parameters
  • SOCKET objects spawned by a SERVER-SOCKET
  • ASYNC-REQUEST(Asynchronous request object handle) handles.
These are covered in more detail below.

TABLE-HANDLE Parameters:

DEFINE....PARAMETER TABLE-HANDLE results in the creation of a TEMP-TABLE object in the receiving procedure.  The caller must delete OUTPUT table handles and the callee must delete INPUT table handles.

For more information on this point, refer to the following Articles:
Sockets:

When a SERVER-SOCKET (a handle to a server socket object which allows listening for and accepting TCP/IP socket connections on a given port) receives a connection, a SOCKET object is automatically created for communication.  The ABL creates it and makes it available for the duration of the session.

The SOCKET object that is created can be accessed using the following means:
  • SESSION:FIRST-SOCKET  -  Returns the handle associated with the first entry in the list of all valid SOCKET objects created in the current session.  If there are no SOCKET objects in the session FIRST-SOCKET returns the unknown value (?).
  • The SELF handle in the READ-RESPONSE procedure defined for the socket.
For more information on this point, refer to the following Article:
APPSERVER ASYNC REQUESTs:

Created in response to a RUN ... ASYNCHRONOUS SET handle.  The ABL creates it and makes it available for the duration of the session.  This handle survives even after the request is complete for later reference in order to check whether the request has completed.

References to asynchronous request objects can be had in one of the following ways: 
  • The handle can be saved to a local variable when executing the RUN statement using the ASYNCHRONOUS SET option.  
  • Reference the LAST-ASYNC-REQUEST attribute on the server handle for the AppServer where the request is running.  To reference a specific request, one must reference the attribute after the associated RUN statement executes and before instantiating another asynchronous request on the same AppServer connection.  
  • hAppServer:FIRST-ASYNC-REQUEST  -  This returns a handle that references the first asynchronous request object on a potential chain of requests being worked on by the specified AppServer connection.  This object is instantiated when executing an asynchronous remote procedure using the RUN statement specified with the ASYNCHRONOUS option.  The handle returned by this method can be used to walk the chain between the FIRST-ASYNC-REQUEST and LAST-ASYNC-REQUEST attributes of the associated server handle.
            Search on the PROCEDURE-NAME attribute of each request handle to identify the specific request.
            Without the DELETE OBJECT statement below, the hRequest handle would never be deleted.
RUN loadTable IN hProc
              ASYNCHRONOUS SET hRequest
              EVENT-PROCEDURE "LoadTT" (OUTPUT TABLE-HANDLE hTT).
WAIT-FOR PROCEDURE-COMPLETE OF hRequest.
DELETE OBJECT hRequest.

 
Workaround
Notes
Last Modified Date8/30/2019 3:00 PM
Attachment 
Files
Disclaimer The origins of the information on this site may be internal or external to Progress Software Corporation (“Progress”). Progress Software Corporation makes all reasonable efforts to verify this information. However, the information provided is for your information only. Progress Software Corporation makes no explicit or implied claims to the validity of this information.

Any sample code provided on this site is not supported under any Progress support program or service. The sample code is provided on an "AS IS" basis. Progress makes no warranties, express or implied, and disclaims all implied warranties including, without limitation, the implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample code is borne by the user. In no event shall Progress, its employees, or anyone else involved in the creation, production, or delivery of the code be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample code, even if Progress has been advised of the possibility of such damages.