Fasterj

|home |articles |cartoons |site map |contact us |
Tools: | GC log analysers| Multi-tenancy tools| Books| SizeOf| Thread analysers|

Our valued sponsors who help make this site possible
New Relic: Try free w/ production profiling and get a free shirt! 

ManageEngine's Site24x7: End-to-End analysis on Java EE web transactions. Sign up for FREE! 

AppDynamics: Get complete browser to backend visibility. Monitor Now! 

The Secret Life Of The Finalizer: page 1 of 2

Chart Java Jitter with jHiccup
Monitor and identify pauses in your Java apps. Download now

Use jKool analytics as a service
Spot patterns in time-series data - real-time and free

JProfiler
Get rid of your performance problems and memory leaks!


See Your Message Here
You could have your tool advertised here, to be seen by thousands of potential customers

Java Performance Tuning, 2nd ed
The classic and most comprehensive book on tuning Java

Java Performance Tuning Newsletter
Your source of Java performance news. Subscribe now!
Enter email:


New Relic
New Relic: Try free w/ production profiling and get a free shirt!

ManageEngine
ManageEngine's Site24x7: End-to-End analysis on Java EE web transactions. Sign up for FREE!

AppDynamics
AppDynamics: Get complete browser to backend visibility. Monitor Now!


Chart Java Jitter with jHiccup
Monitor and identify pauses in your Java apps. Download now

Use jKool analytics as a service
Spot patterns in time-series data - real-time and free

JProfiler
Get rid of your performance problems and memory leaks!


In this article, Jack Shirazi looks into exactly what the JVM does when you create a finalizable object and then have it garbage collected. And it is surprisingly bizarre at times.
Published November 2007, Author Jack Shirazi

Page 1 of 2
next page: The Finalizer Lifecycle

In this article, I'm going to have a look into exactly what the JVM does when you create a finalizable object, following its lifecycle through until it gets garbage collected. Note this article only covers what happens in the Sun JVM, other JVMs may differ in procedure.

A Simple Example

I'll start with a small class that doesn't cause any finalization, so that we can clearly see the differences. My Test1 class (listing 1) is a simple loop that just creates and dereferences instances of Test1. It runs just as you would expect it to - creating lots of garbage instances, with the garbage collector kicking in periodically to clean up the garbage.

public class Test1
{
  static long NumberOfCreatedInstances = 0;
  static long NumberOfDeletedInstances = 0;
  public Test1() {NumberOfCreatedInstances++;}

  static public void main(String args[])
  {
    System.out.println("starting....");
    for (int i = 0; ; i++) {
      Test1 obj = new Test1();
      obj = null;
      if (i%10000000 == 0)
      {
        System.out.println(
          NumberOfCreatedInstances-NumberOfDeletedInstances);
      }
    }
  }
}
Listing 1: The Test1 class

Now I'll change Test1 very slightly by making it finalizable. I've called this class Test2 (listing 2). I've added just one tiny method, highlighted in the code. Doesn't seem like much, I don't even call that method from my code. But as I'm sure you all know, it's the special finalize() method that is called by the garbage collector when the object can be reclaimed.

class Test2
{
  static long NumberOfCreatedInstances = 0;
  static long NumberOfDeletedInstances = 0;
  public Test2() {NumberOfCreatedInstances++;}

  static public void main(String args[])
  {
    System.out.println("starting....");
    for (int i = 0; ; i++) {
      Test2 obj = new Test2();
      obj = null;
      if (i%10000000 == 0)
      {
        System.out.println(
          NumberOfCreatedInstances-NumberOfDeletedInstances);
      }
    }
  }

  protected void finalize()
  {
	  NumberOfDeletedInstances++;
  }

}
Listing 2: The Test2 class - the only difference from the Test1 class is the addition of a finalize() method

It seems like there is not much of a difference between Test1 and Test2. But if I run the two programs, (the -verbose:gc option is particularly useful here), then I see very very different activities between the two invocations. Test1 happily sails along, creating objects continuously, interrupted occasionally by very fast young generations GCs - just exactly as you would expect from looking at the code.

Test2, on the other hand, slows down to a crawl in comparison (you may want to limit the heap to 64m with -Xmx64m, or even lower). If you run Test2 on a JVM earlier than a 1.6 JVM, then Test2 will very likely produce an OutOfMemoryError! (It might take a while.) On a 1.6+ JVM, it will probably limp along indefinitely, going quite slowly, or it might produce an OutOfMemoryError, depending on the system you run it on.

You might well be expecting this pattern if you have previously read about or encountered the cost of finalizers. Or maybe it's a surprise. It was certainly a surprise to me when I first saw it - a Java program that has very little code, and is definitely not holding on to more than one object according to the code, produces an OutOfMemoryError! Even those of you who were expecting that might still be surprised at the exact details of what is happening here.

A Simple Lifecycle

Let's start with Test1. This is a straightforward class, with instances that have a straightforward lifecycle. In the main() method, a Test1 object is created in the Eden space of the young generation at new Test1() (see figure 1). Then the explicit dereference in the very next line (obj = null;) eliminates any reference to that object (actually if that line wasn't there, the dereference would occur in the next loop iteration when obj is set to point at the next Test1 instance, but I wanted to be explicit in the example).


New objects get created in Eden space
Figure 1: New objects getting created in Eden space

At some point, we have created enough Test1 instances that Eden gets full - and that triggers a young generation garbage collection (usually termed "minor GC"). As nothing points to any of the objects in Eden (or possibly one instance is still referenced depending on when the GC occurs), Eden is simply set to empty very efficiently by the garbage collector (if one object is referenced, that single object will be copied to a survivor space first, see figure 2) And presto, we are very efficiently back to an empty Eden, and the main loop continues with further object creation.


A minor GC moves live objects into Survivor space and empties Eden
Figure 2: A minor GC empties out Eden

Page 1 of 2
next page: The Finalizer Lifecycle


Last Updated: 2014-10-29
Copyright © 2007-2014 Fasterj.com. All Rights Reserved.
All trademarks and registered trademarks appearing on Fasterj.com are the property of their respective owners.
URL: http://www.fasterj.com/articles/finalizer1.shtml
RSS Feed: http://www.JavaPerformanceTuning.com/newsletters.rss
Trouble with this page? Please contact us