Based on: Memory Conservation by CJ Ganier
Summary: This module explains the different kinds of memory, some common memory organizations, and the basics of conserving memory.
In the early days of computers, the instruction memories of main frames were incredibly small by today's standards, only in the hundreds or thousands of bytes. This small capacity emphasized making each instruction count and each value saved necessary. Fortunately, just as processors have exponentially increased in speed, program memory has similarly increased in capacity. Even though it seeme like there is an abundance, there are still general practices that must be kept in mind when using program memory. Also, smaller platforms, like microcontrollers, are still limited to memory capacities in the kilobytes. This module explains the kinds of memory, common memory organizations, and the basics of conserving memory.
In most memory architectures there is some categorization of memory into parts. The basic principle behind this is that seperate sections of memory can serve specific purposes while each section can be more efficiently accessed. Types of memory include the following:
The organization of memory can vary among compilers and programming languages. In most cases, the goal of memory management systems is to make the limited resource of memory appear infinite (or at least more abundant) than it really is. The goal is to free the application programmer from having to worry about where his memory will come from. In the oldest days of mainframes, when each byte of memory was precious, a programmer might account each address in memory himself to ensure that there was enough room for the instructions, heap, and data. As programming languages and compilers were developed, algorithms to handle this task were developed so that the computer could handle its own memory issues.
Today, even assembly programmers do not have to worry about memory allocation because current assemblers can handle that task. Memory allocation algorithms are good enough at their job that it isn't worth a programmer's time to manually allocate memory. There are a few different ways that languages solve the problem of memory allocation. In general, it is simply a matter of providing the programmer with memory that is known to be required at compile time including space for global data values and the code itself. The more difficult problem is how to provide flexible data memory that may or may not be needed when the program actually executes.
The approach that C takes is to make available to the the programmer special functions that manage memory allocation. These methods are called malloc(int) (memory allocate) and free(void *) . The basic idea is that whenever the programmer needs a specific amount of additional memory, he calls malloc(int) with the integer being the number of bytes of memory needed. The function returns a pointer to a block of memory of the requested size. When the programmer is done with a particular block of memory, he may call free(void*) to let the memory management library know that the particular block of memory isn't needed anymore by passing the pointer to that block to the function. If the programmer is diligent about returning (freeing) memory that isn't needed anymore, then he will enjoy an abundant supply of memory without having to count individual bytes. On the other hand, if a programmer repeatedly requests memory but does not free the memory back to the system, the memory allocator will eventually run out of memory and program will then crash. Thus, it is essential for passages of code that frequently request memory allocations to free these allocations as quickly as they can. Un-freed memory blocks are not fatal in very infrequently executed parts of code; however, the longer a program runs, the more potential there is for a problem. In general, a program that allocates but does not free memory, is said to have a memory leak.
Other languages handle the problem of memory allocation automatically. Java allocates the memory for new data on the fly using the keyword new instead of the function malloc, but the more important difference is that freeing takes place automatically. Part of the Java system called the garbage collector detects memory that can be safely freed and does so. In this manner, Java programs do not suffer memory leaks in the way a C program might.
In the ez430 there is no inherent difference between instruction memory, data memory, and heap memory. The only subdivisions in memory are the blocks of flash and the sections of RAM. Any of these sections can hold any type of memory; however, because it is problematic to erase and rewrite flash in the middle of program execution, the flash memory is best saved for instructions and constants. The remaining RAM must be shared then between the heap, the dynamically allocated memory, and the global variables. On the ez430, there is only 2KB of RAM, so no memory leaks are tolerable.
The most notable way to waste memory, memory leaks, have already been discussed, but there are several others. While memory leaks abuse the dynamically allocated portion of data memory, many layers of function calls abuse the heap. Above, it was explained that each time a function calls another function, the caller's registers and data are moved onto the heap. If each called function calls another function in turn, then the heap portion of the memory will grow significantly. For high power computing systems, this is not usually a great threat to the overall supply of memory compared to other memory leaks. Embedded systems, however, must avoid deep layers of function calling or risk exhausting the overall supply of memory.
There is a programming technique called recursion, which uses deep layers of function calling, where a function calls itself repeatedly on progressively smaller or simpler versions of the data until the answer is trivial (a base case). While this technique leads to some very clever solutions to some complex problems, it is uses large amounts of memory to achieve this end. Therefore, recursion is generally a poor choice when dealing with microcontrollers.
Another way to waste memory is to create too many global variables. Specifically, variables whose scope could be local to a function or that could be allocated dynamically waste memory because they take up space even when not in use.
"This is the entire course organized at Rice University for all the basic lessons for using an MSP430. It is designed for the use of an eZ430 tool and is still under construction."