Cleanup: finalization and garbage collection | CodeGuru

Cleanup: finalization and garbage collection

Bruce Eckel’s Thinking in Java Contents | Prev | Next garbage collection Programmers know about the importance of initialization, but often forget the importance of cleanup. After all, who needs to clean up an int? But with libraries, simply “letting go” of an object once you’re done with it is not always safe. Of course, […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
8 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

garbage
collection

Programmers


know about the importance of initialization, but often forget the importance of


cleanup. After all, who needs to clean up an


int

?


But with libraries, simply “letting go” of an object once


you’re done with it is not always safe. Of course, Java has the

garbage
collector to reclaim the memory of objects that are no longer used. Now
consider a very special and unusual case. Suppose your object allocates
“special” memory without using
new.
The garbage collector knows only how to release memory allocated
with
new,
so it won’t know how to release the object’s “special”
memory. To handle this case, Java provides a method called
finalize( )
that you can define for your class. Here’s how it’s
supposed
to work. When the garbage collector is ready to release the storage used for
your object, it will first call
finalize( ),
and only on the next garbage-collection pass will it reclaim the object’s
memory. So if you choose to use
finalize( ),
it gives you the ability to perform some important cleanup
at
the time of garbage collection
.

This


is a potential programming pitfall because some programmers, especially C++


programmers, might initially mistake


finalize( )

for the

destructor
in C++, which is a function that is always called when an object is destroyed.
But it is important to distinguish between C++ and Java here, because in C++
objects
always get destroyed

(in a bug-free program), whereas in Java objects do not always get
garbage-collected. Or, put another way:

Garbage


collection is not destruction.

If


you remember this, you will stay out of trouble. What it means is that if there


is some activity that must be performed before you no longer need an object,


you must perform that activity yourself. Java has no destructor or similar


concept, so you must create an ordinary method to perform this cleanup. For


example, suppose in the process of creating your object it draws itself on the


screen. If you don’t explicitly erase its image from the screen, it might


never get cleaned up. If you put some kind of erasing functionality inside


finalize( )

,


then if an object is garbage-collected, the image will first be removed from


the screen, but if it isn’t, the image will remain. So a second point to


remember is:

Your


objects might not get garbage collected.

You


might find that the storage for an object never gets released because your


program never nears the point of running out of storage. If your program


completes and the garbage collector never gets around to releasing the storage


for any of your objects, that storage will be returned to the operating system


en
masse

as the program exits. This is a good thing, because garbage collection has some


overhead, and if you never do it you never incur that expense.


What
is finalize( ) for?

You


might believe at this point that you should not use


finalize( )

as a general-purpose cleanup method. What good is it?

A


third point to remember is:

Garbage


collection is only about memory.

That


is, the sole reason for the existence of the garbage collector is to recover


memory that your program is no longer using. So any activity that is associated


with garbage collection, most notably your


finalize( )

method, must also be only about memory and its deallocation.

Does


this mean that if your object contains other objects


finalize( )

should explicitly release those objects? Well, no – the garbage collector


takes care of the release of all object memory regardless of how the object is


created. It turns out that the need for


finalize( )

is limited to special cases, in which your object can allocate some storage in


some way other than creating an object. But, you might observe, everything in


Java is an object so how can this be?

It


would seem that


finalize( )

is in place because of the possibility that you’ll do something C-like by


allocating memory using a mechanism other than the normal one in Java. This can


happen primarily through


native
methods

,


which are a way to call non-Java code from Java. (Native methods are discussed


in Appendix A.) C and C++ are the only languages currently supported by native


methods, but since they can call subprograms in other languages, you can


effectively call anything. Inside the non-Java code, C’s


malloc( )

family of functions might be called to allocate storage, and unless you call


free( )

that storage will not be released, causing a memory leak. Of course,


free( )

is a C and C++ function, so you’d need call it in a native method inside


your


finalize( )

.

After


reading this, you probably get the idea that you won’t use


finalize( )

much. You’re correct; it is not the appropriate place for normal cleanup


to occur. So where should normal cleanup be performed?


You
must perform cleanup

To


clean up an object, the user of that object must call a

cleanup
method at the point the cleanup is desired. This sounds pretty straightforward,
but it collides a bit with the C++ concept of the
destructor.
In C++, all objects are destroyed. Or rather, all objects
should
be

destroyed. If the C++ object is created as a local, i.e. on the stack (not
possible in Java), then the destruction happens at the closing curly brace of
the scope in which the object was created. If the object was created using
new
(like in Java) the destructor is called when the programmer calls the C++
operator
delete
(which doesn’t exist in Java). If the programmer forgets, the destructor
is never called and you have a memory leak, plus the other parts of the object
never get cleaned up.

In


contrast, Java doesn’t allow you to create local objects – you must


always use


new

.


But in Java, there’s no “delete” to call for releasing the


object since the garbage collector releases the storage for you. So from a


simplistic standpoint you could say that because of garbage collection, Java


has no destructor. You’ll see as this book progresses, however, that the


presence of a

garbage
collector does not remove the need or utility of destructors. (And you should
never call
finalize( )
directly, so that’s not an appropriate avenue for a solution.) If you
want some kind of cleanup performed other than storage release you must
still
call a method in Java, which is the equivalent of a C++ destructor without the
convenience.

One


of the things


finalize( )

can be useful for is observing the process of garbage collection. The following


example shows you what’s going on and summarizes the previous


descriptions of garbage collection:

//: Garbage.java
// Demonstration of the garbage
// collector and finalization
 
class Chair {
  static boolean gcrun = false;
  static boolean f = false;
  static int created = 0;
  static int finalized = 0;
  int i;
  Chair() {
    i = ++created;
    if(created == 47)
      System.out.println("Created 47");
  }
  protected void finalize() {
    if(!gcrun) {
      gcrun = true;
      System.out.println(
        "Beginning to finalize after " +
        created + " Chairs have been created");
    }
    if(i == 47) {
      System.out.println(
        "Finalizing Chair #47, " +
        "Setting flag to stop Chair creation");
      f = true;
    }
    finalized++;
    if(finalized >= created)
      System.out.println(
        "All " + finalized + " finalized");
  }
}
 
public class Garbage {
  public static void main(String[] args) {
    if(args.length == 0) {
      System.err.println("Usage: n" +
        "java Garbage beforen  or:n" +
        "java Garbage after");
      return;
    }
    while(!Chair.f) {
      new Chair();
      new String("To take up space");
    }
    System.out.println(
      "After all Chairs have been created:n" +
      "total created = " + Chair.created +
      ", total finalized = " + Chair.finalized);
    if(args[0].equals("before")) {
      System.out.println("gc():");
      System.gc();
      System.out.println("runFinalization():");
      System.runFinalization();
    }
    System.out.println("bye!");
    if(args[0].equals("after"))
      System.runFinalizersOnExit(true);
  }
} ///:~ 

The


above program creates many


Chair

objects, and at some point after the garbage collector begins running, the


program stops creating


Chair

s.


Since the garbage collector can run at any time, you don’t know exactly


when it will start up, so there’s a flag called


gcrun

to indicate whether the garbage collector has started running yet. A second flag


f

is a way for


Chair

to tell the


main( )

loop that it should stop making objects. Both of these flags are set within


finalize( )

,


which is called during garbage collection.

Two


other


static

variables,


created

and


finalized

,


keep track of the number of


obj

s


created versus the number that get finalized by the garbage collector. Finally,


each


Chair

has its own (non-


static

)


int
i

so it can keep track of what number it is. When


Chair

number 47 is finalized, the flag is set to


true

to bring the process of


Chair

creation


to a stop.

All


this happens in


main( )

,


in the loop

    while(!Chair.f) {
      new Chair();
      new String("To take up space");
    }

You


might wonder how this loop could ever finish, since there’s nothing


inside that changes the value of


Chair.f

.


However, the


finalize( )

process will, eventually, when it finalizes number 47.

The creation of a


String

object during each iteration is simply extra garbage being created to encourage


the garbage collector to kick in, which it will do when it starts to get


nervous about the amount of memory available.

When


you run the program, you provide a command-line argument of


“before” or “after.” The “before” argument


will call the


System.gc( )

method (to force execution of the garbage collector) along with the


System.runFinalization( )

method to run the finalizers. These methods were available in

Java
1.0, but the
runFinalizersOnExit( )
method that is invoked by using the “after” argument is available
only in Java 1.1
[19]
and beyond. (Note you can call this method any time during program execution,
and the execution of the finalizers is independent of whether the garbage
collector runs).

The


preceding program shows that, in Java 1.1, the promise that finalizers will


always be run holds true, but only if you explicitly force it to happen


yourself. If you use an argument that isn’t “before” or


“after” (such as “none”), then neither finalization


process will occur, and you’ll get an output like this:

Created 47
Beginning to finalize after 8694 Chairs have been created
Finalizing Chair #47, Setting flag to stop Chair creation
After all Chairs have been created:
total created = 9834, total finalized = 108
bye!

Thus,


not all finalizers get called by the time the program completes.


[20]

To force finalization to happen, you can call


System.gc( )

followed by


System.runFinalization( )

.


This will destroy all the objects that are no longer in use up to that point.


The odd thing about this is that you call


gc( )
before

you call


runFinalization( )

,


which seems to contradict the Sun documentation, which claims that finalizers


are run first, and then the storage is released. However, if you call


runFinalization( )

first, and then


gc( )

,


the finalizers will not be executed.

One


reason that Java 1.1


might default to skipping finalization for all objects is because it seems to
be expensive. When you use either of the approaches that force garbage
collection you might notice longer delays than you would without the extra
finalization.

[19]

Unfortunately,


the implementations of the garbage collector in Java 1.0 would never call


finalize( )

correctly. As a result,


finalize( )

methods that were essential (such as those to close a file) often didn’t


get called. The documentation claimed that all finalizers would be called at


the exit of a program, even if the garbage collector hadn’t been run on


those objects by the time the program terminated. This wasn’t true, so as


a result you couldn’t reliably expect


finalize( )

to be called for all objects. Effectively,


finalize( )

was useless in Java 1.0.

[20]

By the time you read this, some Java Virtual Machines may show different


behavior.

Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.