By Mukesh Kapoor, Sun Microsystems - Sun ONE Studio Solaris Tools Development Engineering   | 
 | 
 
 
This article discusses some of the runtime errors related to memory  management and how you can use the garbage collection library,  libgc to fix these errors. In most cases, just linking with the library without  making any changes to your  code will fix the errors. You can get additional benefits by calling the functions  available in the public Application Programming Interface (API) of  the library. The library has a C API so you can use it with both C  and C++ programs. libgc is included with the Sun C++ compiler product. | 
 
 
 
Contents 
| - | 
What Is Garbage Collection? | 
 
| - | 
What Does libgc Do? | 
 
| - | 
Common Memory Problems | 
 
| - | 
libgc  API | 
 
| - | 
Modes of Operation | 
 
| - | 
Summary | 
 
| - | 
For More Information | 
 
 
What Is Garbage Collection? 
Garbage collection deals with the automatic management of dynamic  memory. You can dynamically allocate and deallocate memory by using the C++  operators new and delete as well as by using the  libc functions malloc() and free(). Managing  dynamic memory is a complex and highly error prone task and is one of  the most common causes of runtime errors. Such errors can cause intermittent  program failures that are difficult to find and fix. 
A garbage collector automatically analyzes the program's data  and decides whether memory is in use and when to return it to the  system. When it identifies memory that is no longer in use, it  frees that memory. 
The libgc library relieves you from the burden of explicit memory management.  In addition, libgc automatically fixes memory related problems in  third party libraries used in the application. 
What Does libgc Do?  
The libgc library is a conservative garbage collector that  periodically scans a program's data and marks all heap objects that are in use.  Once it has marked all heap objects that are reachable through pointers, it frees  all the objects that have not been marked. It decides when to collect  garbage by monitoring the amount of memory allocated since the last  collection. When enough memory has been allocated, it performs the  garbage collection. 
This way, programs that don't use a lot of dynamic memory won't  spend much time in unnecessary garbage collection. The library has its own  efficient memory allocator. 
Note-
 Do not use an outside allocator with libgc. The  library provides the best performance with its own allocator. It will not  automatically manage memory allocated by other allocators. If you use  your own allocator, libgc will not search that memory for pointers to  garbage collected memory.  
 | 
 
 
Common Memory Problems 
Using libgc fixes problems related to memory management in these  broad categories: 
- Memory leaks
 
- Premature frees
 
- Memory fragmentation
 
 
libgc does not, however,  detect or fix problems due to memory overwrites. 
Memory Leaks 
 This problem occurs if a program fails to free objects that are no  longer in use. The following code leaks 100 bytes of memory every  time the function test is called with the argument flag  as false: 
     void read_file(char*);
     void test(bool flag)
     {
         char* buf = new char[100];
         if (flag) {
     	     read_file(buf);
     	     delete [] buf;
         }
     }
If a program continues to leak memory, its performance degrades.  Its runtime memory footprint continues to increase and it  spends more and more time in swapping and can eventually run  out of memory. 
Premature Frees 
 This is caused by freeing objects that are still in use, and  can result in corrupted data and intermittent failures, including  core dumps.  Once memory is free, it becomes part of the system heap and can  be reallocated later on for use in some other part of the program.  If you try to access memory that has already been freed, you may end up  accessing data in another, completely unrelated, part of the program.  The memory corruption can show up at a time and place  farther away from the actual point of error and can be very difficult  to debug. 
The following code illustrates the problem: 
     void eval(int*);
     void test2()
     {
         int* p1 = new int;
         int* p2 = p1;
         *p1 = 1;
         eval(p1);
         delete p1;
         *p2 = 2;   // the memory pointed to by p2 has already been deleted!
         eval(p2);
     }
A variation of this is the case where the same memory  pointer is deleted more than once: 
     void test3()
     {
         int* p1 = new int;
         int* p2 = p1;
         delete p1;
         delete p2;  // deleting again!
     }
Memory Fragmentation 
 This can be caused by frequent allocation and deallocation of memory  and can degrade an application's performance.  Memory fragmentation occurs when a large chunk of memory is divided  into much smaller, scattered pieces. These smaller discontiguous chunks  of memory may not be of much use to the program and may never be  allocated again. This can result in the program consuming more memory  than it actually allocates. 
Memory Overwrites 
 This problem occurs when too little memory is allocated  for an object. Consequences can include memory corruption and intermittent  failures. The program may work correctly some times and fail  erratically at other times. Here is sample code that shows this problem: 
     void test4()
     {
         char* x = new char[10];
         x[10] = '\0';  // memory overwrite!
     }
libgc does not detect or fix this type of memory problem. You can  use the Run Time Checking (RTC) feature in the debugger to find such  errors. 
libgc  API  
The libgc library has its own implementation of the C memory management  functions malloc(), calloc(), realloc() and  free().  The library also provides the following three functions that can be  called from a user program. You will need to include the header   <gct.h>  if you use any of these functions in your program.  You can use any of the functions listed below in the library's development mode.  See the following section for an explanation of libgc modes.  
void gcFixPrematureFrees(void) 
 Fix premature frees. After this function is called, calls to  free() will not do anything. The pointer passed as argument  to free() will not be freed. The pointer will safely be  freed later on when it is no longer in use. 
Note-
 Fixing premature frees may increase memory usage in  some programs because the library defers the release of memory  until it verifies that the memory is not being used. This is not  an issue for most programs but may be important for some programs  with strict memory constraints.  
 | 
 
 
void gcStopFixingPrematureFrees(void) 
 Stop fixing premature frees. This reverses the effect of calling  gcFixPrematureFrees. 
void gcInitialize(void) 
 This function lets you configure your own settings and it works  slightly differently than the other public API described above. The  difference is that the libgc library calls gcInitialize  at startup time so you should not call gcInitialize directly  in your program. You can define your own function named  gcInitialize in your program as shown in the following example: 
     #include <gct.h>
     . . .
     void gcInitialize(void)
     {
          gcFixPrematureFrees();
     }
Modes of Operation 
The library has two different modes of operation.  In each mode, you can use the libgc library by specifying the  option CC-library=gc or by specifying -lgc at link time.  The -library=gc option is only available with the C++ compiler.  
Deployment Mode 
 You can use this mode when you do not wish to modify your existing code  or if you don't have access to the source code and can't modify it.  To use libgc in deployment mode, link your application with  the library and it automatically fixes memory leaks and memory  fragmentation. It also speeds up memory allocation because  it uses its own efficient malloc() function. 
Development Mode 
 Development mode provides all the benefits of the deployment mode. In  addition, you can write programs without ever calling  delete or free(). The library takes care of freeing  memory that is not in use. This feature provides one of the  benefits of the Java programming language to C and C++ language  programmers. If you are using delete or free() in  your program, you can also fix premature frees in specific regions of  your code by calling the functions gcFixPrematureFrees() and  gcStopFixingPrematureFrees(). 
Summary 
Using libgc automatically provides the following benefits  in the user application: 
- Protects your program against memory leaks.
  Even if the programs use leaky third-party libraries, linking with  libgc automatically fixes the leaks. 
- Allows you to write programs without calling delete or
  free(). This is a benefit of the Java programming  environment which you can exploit for C and C++ programs. 
- Allows you to fix premature frees in your code.
 
- Provides a fast non-fragmenting memory allocator. Programs linked
  with libgc run faster, use less space, and never fragment  memory. 
 
| - | 
Richard Jones and Rafael D Lins, Garbage collection,  Algorithms for Automatic Dynamic Memory Management, Wiley, 1996 | 
 
| - | 
Bjarne Stroustrup, Letter to the ANSI/ISO C++ committee, May 27, 1996. | 
 
 
About the Author 
Mukesh Kapoor   is a staff engineer in the C++ compiler group. He has  an MS (Automation), BS (Electronics and Communication) from the Indian  Institute of Science and BS (Physics) from Delhi University, India.  Before joining Sun, Mukesh held various software development roles  at Peritus Inc, Plexus computers, Elxsi and Unisoft Corp. 
 
 |