Avoiding+resource+leaks

//by Richard Russell, November 2007//

A **resource leak** is the term used when a program uses some finite system resource but doesn't free it again when it has finished with it. For example a program might allocate some memory, and never free it (commonly called a **memory leak**) or it might create a system 'object' but fail to destroy it later.

Resource leaks are insidious, especially in programs which may be left running for a long time (Windows generally, but not always, frees resources on program termination). They can result in the program running perfectly for hours, days, weeks or longer and then failing catastrophically for no apparent reason. If this was not bad enough, resource leaks can affect programs other than the one with the problem, and can even cause Windows itself to crash.

Causes of resource leaks
There are two principal kinds of resource leak which can affect //BBC BASIC for Windows// programs. The first concerns the allocation of memory from BASIC's **heap** using the [|DIM] statement. This is a convenient way of allocating memory (for example in which to store the contents of a file, or a data structure, or a 'fixed string') but the memory isn't freed until the program ends or a **CLEAR** statement is executed (unlike [|DIM LOCAL] which //does// free the memory on exit from the function or procedure in which it was allocated). Therefore it must be done just **once** at the start of the program rather than repeatedly. Consider this example program:

code format="bb4w" DIM ltime% 15, stime% 9 REPEAT SYS "GetLocalTime", ltime% SYS "GetTimeFormat", 0, 0, ltime%, "HH:mm:ss", stime%, 9 PRINT $$stime% WAIT 10 UNTIL FALSE code This program displays the current time, ten times a second, until interrupted by the user. It is a perfectly good program and should run indefinitely without problems.

Compare it with this slightly different example: code format="bb4w" REPEAT DIM ltime% 15, stime% 9 SYS "GetLocalTime", ltime% SYS "GetTimeFormat", 0, 0, ltime%, "HH:mm:ss", stime%, 9 PRINT $$stime% WAIT 10 UNTIL FALSE code The only difference is that the **DIM** statement is now inside the loop. This means that every time the loop is executed (approximately ten times per second) another 26 bytes of heap are allocated, and that memory is //never freed//. This program will appear to run correctly, and indeed it will run for quite some time without problems - approximately one hour with the default setting of HIMEM. But once that time has elapsed the program will fail catastrophically with a **No room** error when all the heap space is exhausted.

The second kind of resource leak which can affect //BBC BASIC for Windows// programs is concerned with calling **Windows API** functions. Many Windows API functions must be used in pairs: typically the first of the pair allocates some resource or creates an object, whilst the second of the pair frees the resource or destroys the object. If you call the first API function but fail to call the second a resource leak will occur. Here are some examples of API functions which //must// be used in pairs:

There are many more!
 * GlobalAlloc||GlobalFree||
 * CreateCompatibleDC||DeleteDC||
 * CreateCompatibleBitmap||DeleteObject||
 * CreateMenu||DestroyMenu||
 * GdipLoadImageFromFile||GdipDisposeImage||
 * InitializeCriticalSection||DeleteCriticalSection||

Sometimes the sequence in which you make the calls is important. For example suppose you create a bitmap and select it into a Device Context thus:

code format="bb4w" SYS "CreateCompatibleDC", @memhdc% TO hdc% SYS "CreateCompatibleBitmap", @memhdc%, width%, height% TO hbm% SYS "SelectObject", hdc%, hbm% TO oldhbm% code When you've finished with the objects you must free them in the correct order:

code format="bb4w" SYS "SelectObject", hdc%, oldhbm% TO hbm% SYS "DeleteObject", hbm% SYS "DeleteDC", hdc% code If instead you were to do the following, you would cause a resource leak:

code format="bb4w" SYS "DeleteDC", hdc% SYS "SelectObject", hdc%, oldhbm% TO hbm% SYS "DeleteObject", hbm% code

Detecting resource leaks
The first kind of resource leak described above (caused by misuse of the **DIM** statement) can be detected using the **Memory usage monitor** described in this article or the functionally equivalent Memory Usage Monitor Utility.

The second kind of resource leak (caused by misuse of **Windows API** functions) can be detected using Windows **Task Manager**. To do that, run Task Manager, select the **Processes** tab, ensure the columns headed **VM Size**, **Handles** and **GDI Objects** are visible (if necessary enable them in the **View... Select columns** menu) and watch the figures in the row corresponding to your program. Some fluctuation is to be expected, but any figure which is rising inexorably indicates a resource leak in your program:



Try running your program multiple times: the displayed values may increase on the first couple of runs but they should then stabilise. If they continue to increase on each run this is again indicative of a resource leak.