JDK-6295525 : Finalization problem in sun.awt.color.ICC_Transform
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 1.4.2,5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2005-07-11
  • Updated: 2011-06-27
  • Resolved: 2006-03-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
1.4.2_35Fixed 6 b77Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
The following problems happen on jdk 1.4.1, 1.4.2, 1.5.0

. Exact steps to reproduce the problem. 
JCK test javasoft.sqe.tests.api.java.awt.Color.getColorSpaceTests fails intermittently with java.awt.color.ProfileDataException when running with "ScavengeALot" option.

The following script can be used to reproduce the problem:
----------------------------------------------
set -x
#!/bin/sh

JHOME=/opt/jdk142
JCK_CLASSES=/proj/tests/java/jck/JCK-14a.fcs/JCK-runtime-14a/classes

let n=0
let p=0
let f=0
let e=0

#exclude sun/awt/color/ICC_Transform.<init>
#exclude java/awt/color/ICC_ColorSpace.toRGB

while [ $n -ne 1 ]
do
let n=$n+1
echo "+++++++++++++++++++++++ $0 / $n / $p / $e +++++++++++++++++++++++"
$JHOME/bin/java_g \
        -XX:+ScavengeALot \
        -XX:ScavengeALotInterval=2 \
        -XX:InitialTenuringThreshold=100 \
        -XX:MaxTenuringThreshold=200 \
        -XX:-UseParallelGC \
        -Xfuture \
        -Xcomp \
        -classpath $JCK_CLASSES \
        javasoft.sqe.tests.api.java.awt.Color.getColorSpaceTests -TestCaseID Color0024
status=$?

echo "Status = $status"

if [ $status -eq 95 ]
then
   let p=$p+1
else
   if [ $status -eq 97 ]
   then
      let e=$e+1
      break
   else
      let f=$f+1
      break
   fi
fi
done

echo "Failed on run $n, exeception = $e"
----------------------------------------------

The test can be made to fail consistently by putting a System.gc() call in sun.awt.color.ICC_Transform class's third constructor (the one that takes an array of ICC_Transform as argument) right before the call to CMM.cmmCombineTransforms().

. Exact text of any error message(s). 
Color0023: Passed. OKAY
java.awt.color.ProfileDataException: Invalid profile sequence
        at sun.awt.color.ICC_Transform.<init>(ICC_Transform.java:115)
        at java.awt.color.ICC_ColorSpace.toRGB(ICC_ColorSpace.java:148)
        at java.awt.Color.<init>(Color.java:516)
        at
javasoft.sqe.tests.api.java.awt.Color.getColorSpaceTests.Color0024(getColorSpaceTests.java:101)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
        at
javasoft.sqe.javatest.lib.MultiTest.invokeTestCase(MultiTest.java:399)
        at javasoft.sqe.javatest.lib.MultiTest.run(MultiTest.java:195)
        at javasoft.sqe.javatest.lib.MultiTest.run(MultiTest.java:127)
        at
javasoft.sqe.tests.api.java.awt.Color.getColorSpaceTests.main(getColorSpaceTests.java:26)
Color0024: Failed. Test case throws exception:
java.awt.color.ProfileDataException: Invalid profile sequence

. Reason for the failure

What happened is:

In java.awt/color/ICC_ColorSpace:

   140      public float[]    toRGB (float[] colorvalue) {
   141
   142          if (this2srgb == null) {
   143              ICC_Transform[] transformList = new ICC_Transform
[2];
   144              ICC_ColorSpace srgbCS =
   145                  (ICC_ColorSpace) ColorSpace.getInstance
(CS_sRGB);
   146              transformList[0] = new ICC_Transform (
   147                  thisProfile, ICC_Transform.Any,
ICC_Transform.In);
   148              transformList[1] = new ICC_Transform (
   149                  srgbCS.getProfile(), ICC_Transform.Any,
ICC_Transform.Out);
   150              this2srgb = new ICC_Transform (transformList);
   151              if (needScaleInit) {
   152                  setComponentScaling();
   153              }
   154          }
   155
                ......
   168      }

The above method calls ICC_Transform constructor on line 150 and passes in the ICC_Transform array "transformList".

Here's what the ICC_Transform constructor looks like:

    97      public ICC_Transform (    ICC_Transform[]    transforms)
    98      {
    99      int        cmmStatus;
   100      long[]    transformIDs;
   101      int        nTransforms, i1;
   102
   103          nTransforms = transforms.length;
   104
   105          transformIDs = new long [nTransforms];
   106
   107          /* put transform IDs into an array */
   108          for (i1 = 0; i1 < nTransforms; i1++) {
   109              transformIDs [i1] = transforms[i1].ID;
   110          }
   111
   112          cmmStatus = CMM.cmmCombineTransforms (transformIDs,
this);
   113
   114          if ((cmmStatus != CMM.cmmStatSuccess) || (ID == 0)) {
   115              throw new ProfileDataException ("Invalid profile
sequence");
   116          }
   117      }

This method calls CMM.cmmCombineTransforms() on line 112.

CMM.cmmCombineTransforms() is a native method. It uses some native data structures that's associated with the two ICC_Transform objects in the array. It gets the native data structures through "transformIDs".

If both of the above java methods are compiled and GC happens when CMM.cmmCombineTransforms is running, the ICC_Transform array and the two objects in the array are not reachable in either of the above two methods, so they are garbage collected, which causes the two ICC_Transform object's finalize() method to run.

The ICC_Transform object's finalize() method causes the native data structures that's associated with it to be freed. But the native method is still using them. So the failure happens.

It's okay for the compiler and the garbage collector to collect the ICC_Transform array because of the following statements in
JSR133 section B.1 "Finalization":

"Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would na����vely be considered reachable. For example, a compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner.

Another example of this occurs if the values in an object��s fields are stored in registers. The program then may access the registers instead of the object, and never access the object again. This would imply that the object is garbage. "

. Proposal for the fix
A reasonalbe fix for this problem would be to pass in the array of objects instead of the array of IDs to the native method. That will guarantee that the objects are not garbage collected until the native method completes.

Other possible fixes include:
1.
    private synchronized void keepAlive() {}
    public ICC_Transform (    ICC_Transform[]    transforms)
    {
        ...
        cmmStatus = CMM.cmmCombineTransforms (transformIDs, this);

        if ((cmmStatus != CMM.cmmStatSuccess) || (ID == 0)) {
            for (i1 = 0; i1 < nTransforms; i1++) {
                transforms[i1].keepAlive();
            }

            throw new ProfileDataException ("Invalid profile sequence");
        }
    }

    public void finalize ()
    {
        synchronized (this) {}
        ...
    }

See JSR133 section B.1 "Finalization" for why the "synchronized" statement is needed in the finalizer.

2. Another possible fix would be to, at the end of the method, assign the object array to a volatile static field, then immediately clear it. And then read the volatile static variable at the beginning of the finalizer.

Of the above choices of fixes, the first one is preferred.

-------------------------------------------------------------

The same test sometime fails for a different reason.
...
# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  SIGSEGV (0xb) at pc=cd4e46e0, pid=10403, tid=1
#
# Java VM: Java HotSpot(TM) Server VM (1.5.0 Beta2 jinteg:08.16.04-
23:24 IA64 debug compiled mode)
# Problematic frame:
# C  [libcmm_g.so+0xc26e0]  fut_lock_fut+0x440
#
The reason for the SIGSEGV is that 2 local variables in the PTCombine function in libcmm (combine.c) were uninitialized. What was happening was that the JVM was storing references to the handles allocated during the scavenges on the stack and the address of the local variables in PTCombine was the same as the handle refs. Thus their contents had been polluted (i.e. non-zero) with an address in the handle area.

The code in PTCombine passed a pointer to one of these unitialized 
variables to another function that *only* initializes the value in 
certain paths. The path we took did not fill a good value in so the contents were still the polluted value. Unfortunately the code in fut_lock_fut only checked if the value passed in was NULL - here it wasn't - but it still wasn't a valid fut_t instance.

Initializing PTData1 and PTData2 in PTCombine to NULL stops the SIGSEGV from occuring.

Also, it is safer to switch the KpMemSet() call and the unlockPTTable() call at the end of deletePTTable() method, so that we don't zero out PTTable until after unlocking it.

###@###.### 2005-07-11 21:38:00 GMT
###@###.### 2005-07-13 09:52:48 GMT

Comments
EVALUATION This problem was fixed by passing array of ICC_Transform objects directly into native code of the CMM library
01-03-2006

EVALUATION Looks like current JLS allows the optimizations which caused the crash. I've considered all the suggested solutions. It seems to me that the only suitable one in this case is using of the keepAlive method which is currently unavailable. So, for the mustang and earlier releases I'm working on alternative solution.
13-02-2006

EVALUATION Comment by ###@###.### transferred to Comments section. The "early finalization" in this case is in fact in line with the current Java specification. Efforts are on-going elsewhere to evolve an official mechanism to protect against this kind of problem. One such is discussed at http://ccc.sfbay/6181943; see also bug id 6181943. In the meanwhile this specific case can be addressed by means of ensuring, as described in this bug report, that the objects in question do not become unreachable (and finalized) before all native methods accessing the native state have completed. When java.lang.System.keepAlive() is availbale, it would provide an alternative mechanism for preventing early finalization.
06-09-2005