Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4827358
Votes 15
Synopsis Use of finalization in JPEGImageReader/Writer degrades performance
Category java:imageio
Reported Against 1.4.1
Release Fixed 1.5(tiger)
State 10-Fix Delivered, bug
Priority: 3-Medium
Related Bugs 4777499 , 4867874
Submit Date 05-MAR-2003
Description




FULL PRODUCT VERSION :
java version "1.4.1_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_02-b06)
Java HotSpot(TM) Client VM (build 1.4.1_02-b06, mixed mode)

FULL OS VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
Memory allocation doesn't wait for finalization to catch up.
Almost all programs run more than 1 thread which is creating objects, meanwhile only one thread is finalizing them, eventually OutOfMemoryErrors are thrown at almost random places throughout the code, making the code extremely fragile.

Changing VM paramters to alter Garbage Collector behaviour makes no difference, the code fails no matter what the parameters.  Increasing the max heap size only delays the problem.

This problem only started to be really bad when JDK 1.4 came out.  The cause of this is that many of the new imageio and nio classes have finalize methods causing them to require finalizing before they can get garbage collected.  Despite the fact the the Finalizer thread runs at a higher priority, these new classes still block for a short amount of time hence finalization is getting blocked.

The more threads allocating objects requiring finalization the worse the problem gets.

Some of the imageio classes are using several meg of memory, which could easily be garbage collected before finalization was run.  Simply have the image classes hold a sub object with a finalize method and a handle such that it can call dispose instead of the image classes directly having the finalize method and hence having to wait for the Finalizer thread before they release several megs of memory.  The problem still needs to get fixed, but this would atleast give programs using the imageio a chance to continue without the bug getting fixed.

Related Bugs: 4811982, 4640743, 4239841

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run code and wait for the OutOfMemoryError

EXPECTED VERSUS ACTUAL BEHAVIOR :
With each of the 2 new threads only using 1 object each, I would expect this code to run forever without a problem.  The code only needs about 2MB to run, yet it throws OutOfMemoryErrors no matter how much memory the VM allocates to it.
Thread dies due to OutOfMemoryError.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
class TestObject {
  private byte[] bytes;
  private static int createdCount;
  private static int finalizedCount;

  public TestObject() {
    bytes = new byte[1000000];
    try {
      Thread.sleep(500);
    }
    catch (InterruptedException ex) {
    }
    System.out.println("Created " + ++createdCount);
  }

  public void finalize() {
    try {
      Thread.sleep(500);
    }
    catch (InterruptedException ex) {
    }
    System.out.println("Finalized " + ++finalizedCount);
  }
}

class TestThread extends Thread {
  public void run() {
    while (true) {
      TestObject testObject = new TestObject();
      // do something with object
      testObject = null; // allow it to get garbage collected
      // continue with something else
    }
  }
}

public class Test {
  public static void main(String[] args) {
    new TestThread().start();
    new TestThread().start();
  }
}
---------- END SOURCE ----------
(Review ID: 182033) 
======================================================================
Work Around
N/A
Evaluation
We can't build a garbage collector for Java that would "wait for finalization
to catch up" before throwing an OutOfMemoryError since finalize() methods can
themselves allocate memory, and we can't prevent that without making some
fairly extreme changes to the language itself.

The invocation of an object's finalize() method can be delayed an arbitrarily
long time from the moment that the object is detected to be finalizable by the
GC.  Objects with finalize() methods also have longer lifetimes since they have
to be kept around long enough for the GC to discover that they're finalizable,
finalize them, and then finally reclaim them.  Finalization is hence, at best,
a back-stop mechanism for deallocating external resources such as I/O or image
buffers.

Owing to the performance overhead of finalization the NIO channel classes were
revised so as not to use it in 1.4.2 (4777499).  I'm forwarding this bug to the
Image I/O team for further evaluation.

--  xxxxx@xxxxx  2003/3/6

We should remove the finalize() methods and instead make use of the Java 2D
Disposer mechanism for disposing native resources.  There are some explicit
System.gc() calls in the JPEG ImageReader/Writer plugins (see bug 4867874)
that can be removed once the Disposer is used.  (These two bugs should be
handled simultaneously...)
 xxxxx@xxxxx  2003-05-23
Comments
  
  Include a link with my name & email   

Submitted On 07-MAR-2003
yoseph_phillips
It is good to see that 4777499 has been fixed, that should
atleast delay the problem.
Fixing imageio as well may solve the problems for us, but I
can see the problems occuring again with something else.

If the concern about waiting for the Finalizer thread is due
to the chance that it may be requesting memory, then it
seems to me it would be keeping to spec that when enough
memory isn't available to allocate immediately to check if
the current thread is a finalizer thread and if it is throw
the OutOfMemoryError as before, if not wait for it to
finish.  This should work nicely as the finalizer thread
already catches all throwables causing that finalize method
to finish and hence allowing the finalizable object to be
reclaimed.
All this means is that some finalizers may not get
completed, which is already documented, as no gaurantee is
made that they will run in the 1st place.


Submitted On 08-MAR-2003
yoseph_phillips
Uncommenting the commented out lines (shown below) will show
what improvements could be made by fixing imageio to release
memory before finalization is done.
Note using an anonymous inner class for the finalizer would
have kept an implicit TestObject.this holding onto the
unwanted memory, hence a static inner class is used instead.

By adding an algorithm such as that in newTestObject():
i) new wont block any threads indefinately 
ii) finalization calling new wont be a problem
iii) programs using SoftReferences to Objects with a
finalizer method will also be able to have their finalizers
run despite the low memory condition

I think both the imageio fix and some new allocation
algorithm should both be done.

class TestObject {
  private byte[] bytes;
  private static int createdCount;
  private static int finalizedCount;

  public TestObject() {
    bytes = new byte[1000000];
    try {
      Thread.sleep(500);
    }
    catch (InterruptedException ex) {
    }
    System.out.println("Created " + ++createdCount);
  }

  //private static class TestObjectFinalizer {
    public void finalize() {
      try {
        Thread.sleep(500);
      }
      catch (InterruptedException ex) {
      }
      System.out.println("Finalized " + ++finalizedCount);
    }
  //};

  //private Object finalizer = new TestObjectFinalizer();
}

class TestThread extends Thread {
  public static TestObject newTestObject() {
    try {
      return new TestObject();
    }
    catch (OutOfMemoryError ex) {
    }
    byte count = 0;
    Runtime runtime = Runtime.getRuntime();
    long maxFreeMemory = runtime.freeMemory();
    while (count++ < 100) {
      try {
	  Thread.sleep(count);
	}
	catch (InterruptedException ex) {
      }
      long freeMemory = runtime.freeMemory() ;
      if (freeMemory > maxFreeMemory) {
        try {
          return new TestObject();
        }
        catch (OutOfMemoryError ex) {
	  }
	  count = 0;
        maxFreeMemory = freeMemory;
	}
    }
    return new TestObject();  
  }

  public void run() {
    while (true) {
      TestObject testObject = newTestObject();
      // do something with object
      testObject = null; // allow it to get garbage collected
      // continue with something else
    }
  }
}

public class Test {
  public static void main(String[] args) {
    new TestThread().start();
    new TestThread().start();
  }
}


Submitted On 26-MAR-2003
rcollinsrealsport
I tried to raise this as an issue about 2 years ago, but got no 
traction. I have a relatively simple workaround that will do a 
good job, except on machines with lots of CPUs. During 
program initialization, I create a garbage instance. In the 
finalize() method, I do "Thread.currentThread().setPriority()" 
to bump the priority of the finalizer thread to be higher than 
my application's threads. This way, the finalizer will finalize 
aggressively, rather than the designed behavior of finalizing 
only when there is an idle CPU.


Submitted On 01-APR-2003
yoseph_phillips
Not even rcollinsrealsport's workaround fixes this problem.
 Due to the finalizer thread already running at a higher
priority than the application threads, the problem is when
the finalizer thread has any blocking in it then other
threads still get to run, hence throw OutOfMemoryError no
matter what.  We still don't even have a workaround.



PLEASE NOTE: JDK6 is formerly known as Project Mustang