发布时间:2022-07-05 文章分类:编程知识 投稿人:赵颖 字号: 默认 | | 超大 打印
Article

Using the C/C++ Garbage Collection Library, libgc


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.
For More Information
- 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.

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.