SunQuest
 
       C++
  Home arrow C++ arrow Page 9 - A Simple Garbage Collector for C++
IBM developerWorks
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Dedicated Servers  
Actuate Whitepapers 
Moblin 
IBM® developerWorks 
Sun Developer Network 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
C++

A Simple Garbage Collector for C++
By: McGraw-Hill/Osborne
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 41
    2005-06-21

    Table of Contents:
  • A Simple Garbage Collector for C++
  • Comparing the Two Approaches to Memory Management
  • Choosing a Garbage Collection Algorithm
  • What About auto_ptr?
  • An Overview of the Garbage Collector Classes
  • GCPtr In Detail
  • The Overloaded Assignment Operators
  • GCInfo
  • How to Use GCPtr
  • Allocating Arrays
  • A Larger Demonstration Program
  • Load Testing

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT

    Stay one step ahead of the competition. Evaluate and give feedback on some of the hottest web development tools on the market today. Make your opinion heard! Click Here

    A Simple Garbage Collector for C++ - How to Use GCPtr


    (Page 9 of 12 )

    Using a GCPtr is quite easy. First, include the file gc.h. Then, declare a GCPtr object, specifying the type of the data to which it will point. For example, to declare a GCPtr object called p that can point to an int, use this kind of declaration:

    GCPtr<int> p; // p can point to int objects

    Next, dynamically allocate the memory using new and assign the pointer returned by new to p, as shown here:

    p = new int; // assign p the address of an int

    You can assign a value to the allocated memory using an assignment operation like this:

    *p = 88; // give that int a value

    Of course, you can combine the three preceding statements like this:

    GCPtr<int> p = new int(88); // declare and initialize

    You can obtain the value of the memory at the location pointed to by p, as shown here:

    int k = *p;

    As these examples show, in general you use a GCPtr just like a normal C++ pointer. The only difference is that you don’t need to delete the pointer when you are through with it. The memory allocated to that pointer will be automatically released when it is no longer needed.

    Here is an entire program that assembles the pieces just shown:

    #include <iostream>
    #include <new>
    #include "gc.h"
    using namespace std;
    int main() {
      GCPtr<int> p;
     
    try {
       
    p = new int;
      } catch(bad_alloc exc) {
        cout << "Allocation failure!\n";
        return 1;
     
    }
     
    *p = 88;
     
    cout << "Value at p is: " << *p << endl;
     
    int k = *p;
     
    cout << "k is " << k << endl;
     
    return 0;
    }

    The output from this program with the display option turned on is shown here. (Recall that you can watch the operation of the garbage collector by defining DISPLAY within gc.h.)

    Constructing GCPtr.
    Value at p is: 88
    k is 88
    GCPtr going out of scope.
    Before garbage collection for gclist<int, 0>:
    memPtr        refcount      value
    [002F12C0]        0         88
    [00000000]        0         ---
    Deleting: 88
    After garbage collection for gclist<int, 0>:
    memPtr        refcount      value
                 -- Empty --

    When the program ends, p goes out of scope. This causes its destructor to be called, which causes the reference count for the memory pointed to by p to be decremented. Because p was the only pointer to this memory, this operation sets the reference count to zero. Next, p’s destructor calls collect( ), which scans gclist, looking for entries that have a reference count of zero. Because the entry previously associated with p has a reference count of zero, its memory is freed.

    One other point: notice that prior to garbage collection, a null pointer entry is also in gclist. This null pointer was created when p was constructed. Recall that if a GCPtr is not given an initial address, the null address (which is zero) is used. Although it is not technically necessary to store a null pointer in gclist (because it is never freed), doing so simplifies other parts of GCPtr because it ensures that every GCPtr has a corresponding entry in gclist.

    Handling Allocation Exceptions

    As the preceding program shows, because the garbage collector does not change the way that memory is allocated via new, you handle allocation failures in the same way as usual, by catching the bad_alloc exception. (Recall that when new fails, it throws an exception of type bad_alloc.) Of course, the preceding program won’t run out of memory, and the try/catch block isn’t really needed, but a real-world program might exhaust the heap. Thus, you should always check for this possibility.

    In general, the best way to respond to a bad_alloc exception when using garbage collection is to call collect( ) to recycle any unused memory and then retry the allocation that failed. This technique is employed by the load-testing program shown later in this chapter. You can use the same basic technique in your own programs.

    A More Interesting Example

    Here is a more interesting example that shows the effect of a GCPtr going out of scope before the end of the program:

    // Show a GCPtr going out of scope prior to the end
    // of the program.
    #include <iostream>
    #include <new>
    #include "gc.h"
    using namespace std;
    int main() {
      GCPtr<int> p;
      GCPtr<int> q;
     
    try {
        p = new int(10);
        q = new int(11);
       
    cout << "Value at p is: " << *p << endl;
        cout << "Value at q is: " << *q << endl;
       
    cout << "Before entering block.\n";
       
    // Now, create a local object.
       
    { // start a block
          GCPtr<int> r = new int(12);
          cout << "Value at r is: " << *r << endl;
       
    } // end the block, causing r to go out of scope
       
    cout << "After exiting block.\n";
      } catch(bad_alloc exc) {
        cout << "Allocation failure!\n";
        return 1;
      }
     
    cout << "Done\n";
      return 0;
    }

    This program produces the following output when the display option is turned on:

    Constructing GCPtr.
    Constructing GCPtr.
    Value at p is: 10
    Value at q is: 11
    Before entering block.
    Constructing GCPtr.
    Value at r is: 12
    GCPtr going out of scope.
    Before garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F31D8]      0       12
    [002F12F0]      1       11
    [002F12C0]      1       10
    [00000000]      0       ---
    Deleting: 12
    After garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F12F0]      1       11
    [002F12C0]      1       10
    After exiting block.
    Done
    GCPtr going out of scope.
    Before garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F12F0]      0       11
    [002F12C0]      1       10
    Deleting: 11
    After garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F12C0]      1       10
    GCPtr going out of scope.
    Before garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F12C0]      0       10
    Deleting: 10
    After garbage collection for gclist<int, 0>:
    memPtr      refcount    value
               -- Empty --

    Examine this program and its output closely. First, notice that p and q are created at the start of main( ), but r is not created until its block is entered. As you know, in C++, local variables are not created until their block is entered. When r is created, the memory to which it points is given an initial value of 12. This value is then displayed, and the block ends. This causes r to go out of scope, which means that its destructor is called. This causes r’s reference count in gclist to be decremented to zero. Then collect( ) is called to collect garbage.

    Because the display option is on, when collect( ) begins, it displays the contents of gclist. Notice that it has four entries. The first is the one that was previously linked to r. Notice that its refcount field is zero, indicating that the memory pointed to by the memPtr field is no longer in use by any program element. The next two entries are still active, and they are linked to p and q. Because they are still in use, the memory they point to is not freed at this time. The final entry represents the null pointer to which p and q originally pointed when they were created. Because it is no longer in use, it will be removed from the list by collect( ). (Of course, no memory is freed when the null pointer is removed.)

    Because no other GCPtr points to the same memory as r, its memory can be released, as the Deleting: 12 line confirms. Once this is done, program execution continues after the block. Finally, p and q go out of scope when the program ends and their memory is released. In this case, q’s destructor is called first, meaning that it is collected first. Finally, p is destroyed and gclist is empty.

    Allocating and Discarding Objects

    It is important to understand that memory becomes subject to garbage collection as soon as its reference count drops to zero (which means that no GCPtr is pointing to it). It is not necessary for the GCPtr that originally pointed to that object to go out of scope. Thus, you can use a single GCPtr object to point to any number of allocated objects by simply assigning that GCPtr a new value. The discarded memory will eventually be collected. For example:

    // Allocate and discard objects.
    #include <iostream>
    #include <new>
    #include "gc.h"
    using namespace std;
    int main() {
      try {
        // Allocate and discard objects.
        GCPtr<int> p = new
    int(1);
        p = new int(2);
        p = new int(3);
        p = new int(4);
       
    // Manually collect unused objects for
        // demonstration purposes.
        GCPtr<int>::collect();
        
    cout << "*p: " << *p << endl;
      } catch(bad_alloc exc) {
        cout << "Allocation failure!\n";
        
    return 1;
      }
      return 0;
    }

    The output from this program, with the display option turned on, is shown here:

    Constructing GCPtr.
    Before garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F1310]      1        4
    [002F1300]      0        3
    [002F12D0]      0        2
    [002F12A0]      0        1
    Deleting: 3
    Deleting: 2
    Deleting: 1
    After garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F1310]      1        4
    *p: 4
    GCPtr going out of scope.
    Before garbage collection for gclist<int, 0>:
    memPtr      refcount    value
    [002F1310]      0        4
    Deleting: 4
    After garbage collection for gclist<int, 0>:
    memPtr      refcount    value
              
    -- Empty --

    In the program, p, a GCPtr to int, is assigned a pointer to four separate chunks of dynamic memory, each being initialized with a different value. Next, a call is made to collect( ), which forces garbage collection to take place. Notice the contents of gclist: three of the entries are marked inactive, and only the entry that points to the memory that was allocated last is still in use. Next, the unused entries are deleted. Finally, the program ends, p goes out of scope, and the final entry is removed.

    Notice that the first three chunks of dynamic memory to which p pointed have reference counts of zero. This is because of the way the overloaded assignment operator works. Recall that when a GCPtr is assigned a new address, the reference count for its original value is decremented. Thus, each time p is assigned the address of a new integer, the reference count for the old address is reduced.

    One other point: because p was initialized when it was declared, no null-pointer entry was generated and put on gclist. Remember, a null-pointer entry is created only when a GCPtr is declared without an initial value.

    More C++ Articles
    More By McGraw-Hill/Osborne


       · Your copy constructor and assignment operator are much too complicated and add lots...
       · I just worked on implementing your GC and testing it out in some of my code. I...
     

    Buy this book now. This article was excerpted from chapter two of The Art of C++, written by Herbert Schildt (McGraw-Hill/Osborne, 2004; ISBN: 0072255129). Check it out at your favorite bookstore today. Buy this book now.

    C++ ARTICLES

    - Multiplying Large Numbers with Karatsuba`s A...
    - Large Numbers
    - Dijkstra`s Shunting Algorithm with STL and C...
    - Brief Introduction to the STL Containers
    - The Standard Template Library
    - Templates in C++
    - C++ Programmer Alerts
    - C++ Programming Tips
    - First Steps in (C) Programming, conclusion
    - First Steps in (C) Programming, continued
    - First Steps in (C) Programming, introduction
    - C++ Preprocessor: Always Assert Your Code Is...
    - C++ Preprocessor: The Code in the Middle
    - Programming in C
    - Temporary Variables: Runtime rvalue Detection







    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 2 hosted by Hostway