Home | Contact | Pricing | News | Events | Partners | Mailing List | Site Map
Gnat Pro. Powerful tools. Frontline Support. Ada expertise.

Gem #77: Where did my memory go? (Part 1)

Author: Emmanuel Briot, AdaCore

Abstract: Ada Gem #77 — A number of tools and libraries exist to monitor memory usage, detect memory leaks, and more generally solve issues with memory management. This Gem, and others to follow, offer an overview of these issues and explain how you can benefit from these tools in your own development.

« Previous Gem | Next Gem » | Gems Menu

Let’s get started…


Unless your coding standard forbids any dynamic allocation, memory management is a constant concern during system development. You might want to limit the amount of memory that your application requires, or you might have memory leaks (allocation chunks that are never returned to the system). The latter is a critical concern for long-running applications.

Part I: Storage Pools

The standard Ada memory management mechanism is the storage pool, as defined in package System.Storage_Pools. A storage pool is a tagged type that allows you to override the standard “new” operator and the associated Unchecked_Deallocation procedure. A given pool can be associated with one or more access types. The GNAT run-time comes with a number of predefined storage pools, and you can also create your own. One basic implementation, for instance, would be to instrument the pool operations to print a trace to the console every time memory is allocated or freed, and then post-process those traces with an external tool afterward.

This is of course a little tedious, so GNAT provides the package GNAT.Debug_Pools as a much more advanced storage pool implementation that can, at any time during program execution, display the currently allocated memory (along with a backtrace of the code at the point of allocation). It will also detect invalid memory references (for instance, attempting to dereference a pointer to already deallocated memory). The implementation is efficient andl imposes only a very small overhead on your application.

Here is a short example demonstrating the use of debug pools:

with GNAT.Debug_Pools;

package My_Package is
   Pool : GNAT.Debug_Pools.Debug_Pool;

   type Integer_Access is access Integer;
   for Integer_Access'Storage_Pool use Pool;
end My_Package;

with My_Package;
with Ada.Unchecked_Deallocation;
procedure Main is
   procedure Unchecked_Free is
      new Ada.Unchecked_Deallocation (Integer, Integer_Access);
   Ptr : Integer_Access;
begin
   Ptr := new Integer;
   Ptr.all := 1;
   Unchecked_Free (Ptr);
   Ptr.all := 2;  --  raises exception
end Main;

The variable My_Package.Pool should be shared as much as possible among all your access types. It’s not necessary to create one per access type.

As noted in the main procedure, the last reference to Ptr is invalid, and will result in an exception raised from the debug pool (rather than some erroneous behavior depending on the system).

GNAT.Debug_Pools provides various subprograms to analyze current memory usage, in particular the total amount of memory currently allocated, as well as which part of the code did the allocations. The backtraces are also useful when analyzing double-deallocation scenarios, since a debug pool shows both where the memory was allocated and by what piece of code it was first deallocated.

However, GNAT’s debug pools are rather heavy to put in place in existing code, since you need to add a “for Type_Name’Storage_Pool use Pool” to every access type, and there is no mechanism to define a single default storage pool for all types. GNAT.Debug_Pools can also give false warnings when dereferencing a pointer to aliased data on the stack (which was never allocated via a “new” operator, but was accessed via an ‘Access attribute).

In the next Gem we will discuss an alternative approach to controlling and instrumenting dynamic allocation and deallocation, by overriding the low-level memory management support itself.

 

Posted by Posted in Ada / Ada 2005, Development Log, Devt log - Gem of the Week

Have your own idea for a Gem?

If you have an idea for a Gem you would like to contribute please feel free to contact us at: gems@adacore.com

Discussion

6 responses to “Gem #77: Where did my memory go? (Part 1)”


  1. Duncan Sands said:

    I’m not sure that the example says anything about Debug_Pools:
    the call to Unchecked_Deallocation will set Ptr to null, so
    you would get an exception here whether you used Debug_Pools
    or not.


  2. Emmanuel Briot said:

    Yes, this is true. In fact, the example might useful be changed into

    Ptr2 : Integer_Access;
    Ptr := new Integer;
    Ptr2 := Ptr;
    Unchecked_Free (Ptr);
    Ptr2.all := 2; — raises exception


  3. Anh Vo said:

    Of course, there is missing use My_Package clause in the Main subprogram.

    It is rather interesting to find out that (Prt2.all := 2;)statement raising GNAT.Debug_Pools.Accessing_Deallocated_Storage exception. However, (Prt.all := 1;) statement raises Constraint_Error.

    Why do they trigger different exception?


  4. Emmanuel Briot said:

    As pointed out by Duncan, Ptr has been reset to null, so you get a Constraint_Error. Ptr2, however, still has a non null value, and therefore you get another exception. The good thing is that with a different exception it is clearer where the probleme is


  5. Anh Vo said:

    I agree that it is a good thing to raise GNAT.Debug_Pools.Accessing_Deallocated_Storage exception. In fact, if GNAT.Debug_Pools.Debug_Pool is not used (using default pool), the problem is not detected, and exception is not raised at all.


  6. Anh Vo said:

    It just occurred to me that it could be a memory leak if exception is not raised when accessing (Ptr2.all) Ptr2 when normal memory (default pool) used.

Leave a Reply