Memory Management In Embedded Systems


Embedded systems software has many aspects which distinguish it from other types of software systems. It is difficult to understate the importance of memory management in these types of systems. The fact that embedded systems should be able to operate indefinitely imposes a set of requirements that are not as important  for other types of software systems. Forces that degrade system resources over time are of particular interest in systems that execute over a long period of time.


Heap Fragmentation

Programs, which have a limited execution interval, either by design or by user acceptance, are not subject to the same constraints imposed upon embedded applications. Batch oriented aapplications, such as compilers, run for a limited time and are subsequently restarted with a bright shiny new heap upon their next invocation. Interactive programs, or the platform upon which they run, are restarted relatively frequently providing an oportunity for rotting heap structures to be renewed. Although this is not a desirable feature, it has become widely accepted by users that this is simply a reality with which they must live.

Traditional dynamic memory management operations such as malloc()/free() and new/delete, while superficially easy to use, mask potential problems and oversimplify issues surrounding memory management that results in many insidious side effects.

Unless an application executes in a memory space that is constrained relative to the needs of the program, or the program is especially long running, or if the program allocates and frees dynamic memory resources of varying sizes at a high frequency, it is unlikely to encounter the effects of heap fragmentation. Furthermore, when the effects of heap fragmentation are encountered, it is much more common to relieve the symptoms than to solve the problem. The most common solutions are to require the user to have more memory, or to suggest that the customer restart the program or the system upon which the program executes. Sound familiar? Unfortunately, this type of solution only reduces the frequency of failure to an "acceptable" threshold.

Embedded systems are long running programs that typically execute in a resource (e.g. memory) constrained environment. Periodically rebooting systems, either manually or automatically, not only may tarnish the reputation of the system's vendor, but may also endanger lives either directly or indirectly. Thus, it is important that we solve the underlying problem and not simply attempt to reduce its frequency.

Memory Leaks

Another source of entropy in memory management is the so called memory leak. Software designs that have incosistent or ad-hoc dynamic memory management strategies are subject to program errors where dynamically allocated memory resources are not released. The result is that over time, the application can no longer obtain the memory resources it needs to operate.

Since the effects of memory leaks can take a long period of time before they manifest themselves in a way that indicates a failure, such software defects may remain latent until encountered in a customer installation. Find these defects can be quite difficult, and repairing the defect in one place can cause a failure in another part of the program where the assumptions about the responsiblity for freeing the allocated memory were different.

It is for this reason that we recommend a design rule in which the client owns and manages its own memory resources, and is responsible for both the allocation and deallocation of its memory. Notice that this implies that memory should never be allocated by one thread and subsequently freed by another thread, except by design exception.


Allocation Times

Real-time systems are embedded systems with time deadlines and behaviors that are sensitive to execution timing. Heap fragmentation .

Resource Availability



Polling For Memory

Another problem with traditional memory managment operations is that they are not designed for an event driven environment. There is no means for notifying the program when memory is available. This leaves the designer with little choice but to poll for available memory (evil), or simply fail. While either of these may be tolerated in other software systems, they are simply not acceptable in a system with limited processor bandwidth and high availability requirements.