United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4646747 Wrong getPersistenceDelegate() return after memory stress
JDK-4646747 : Wrong getPersistenceDelegate() return after memory stress

Details
Type:
Bug
Submit Date:
2002-03-04
Status:
Resolved
Updated Date:
2002-04-27
Project Name:
JDK
Resolved Date:
2002-04-27
Component:
client-libs
OS:
windows_2000
Sub-Component:
java.beans
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.4.0
Fixed Versions:
1.4.1 (hopper)

Related Reports
Relates:
Relates:

Sub Tasks

Description

Name: nt126004			Date: 03/04/2002


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

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]



A DESCRIPTION OF THE PROBLEM :
XMLEncoder looses custom PersistenceDelegate after
intensive memory allocation. It works as expected under
1.4.0beta3 but fails on 1.4.0RC1.
In the provided test program, I create new 1MB array in
loop and check whether or not getPersistenceDelegate()
returns the same object, i've put by
setPersistenceDelegate() earlier.
Test output shows, that:
1. on 1.4.0RC1 check fails after third allocation. (because
JVM starts by default with 2M memory pool)
2. on 1.4.0Beta3 check is OK on 10 allocations.



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.Compile provided class.

2.Run test:

java persistence_test.Test



EXPECTED VERSUS ACTUAL BEHAVIOR :
Output on 1.4.0RC1:
Before memory allocation: OK
0 OK
1 OK
2 Different: persistence_test.Test$1 :
java.beans.DefaultPersistenceDelegate
Test finished

Output on 1.4.0beta3:
Before memory allocation: OK
0 OK
1 OK
2 OK
3 OK
4 OK
5 OK
6 OK
7 OK
8 OK
9 OK
Test finished

Note:

The actual results would depend on how many memory JVM
allocated on startup on your system...


This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package persistence_test;

import java.beans.*;
import java.io.*;


public class PersistenceTest {

   
    public static void main(String[] args){
        final int ARRAYSIZE = 1000000;
        final int BUFERLENGTH = 10;
        
        try{
            XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new
ByteArrayOutputStream()));
            PersistenceDelegate pd  = new DefaultPersistenceDelegate(){
                protected Expression instantiate(Object oldInstance, Encoder
out) {
                    return super.instantiate(oldInstance, out);
                }
                protected void initialize(Class type, Object oldInstance,
Object newInstance, Encoder out) {
                    super.initialize(type, oldInstance, newInstance, out);
                }
            };
            encoder.setPersistenceDelegate(Test.class, pd);
            Object[] bufArray = new Object[BUFERLENGTH];
            System.out.print("Before memory allocation: ");
            checkClasses(pd, encoder.getPersistenceDelegate(Test.class));
            for(int i=0; i < BUFERLENGTH; i++){
                bufArray[i] = new byte[ARRAYSIZE];
                System.out.print(i + " ");
                if (checkClasses(pd, encoder.getPersistenceDelegate
(Test.class))) break;
            }
            encoder.close();
            System.out.println("Test finished");
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
    
    public static boolean checkClasses(PersistenceDelegate pd1,
PersistenceDelegate pd2){
        if (pd1 == pd2)
            System.out.println("OK");
        else{
            System.out.println("Different: "  + pd1.getClass().getName()
+ " : " + pd2.getClass().getName());
        }
        return pd1 != pd2;
    }
}


---------- END SOURCE ----------

CUSTOMER WORKAROUND :
The possible ugly work around is increasing JVM startup
memory amount with -Xms key.
(Review ID: 139123) 
======================================================================

                                    

Comments
EVALUATION

This represents a serious failure and I am well aware of this bug and will fix it for 1.4.1. I fixed a similar JCK failure for internal PeristenceDelegates (4525285), however, this failure is applicable to user set PersistenceDelegates on the Encoder.

The problem stems from the fix to a showstopper 4508780 in which the static BeanInfo cache had a memory leak. To fix the memory leak, a WeakHashMap was used and so that the keys could be gc'd if there were no strong references left. Another part of the solution involved wrapping the BeanInfo values with SoftReferences. This is the essence of the problem as the SoftReferenced BeanInfos would be lost as the memory of the system is stressed.

A solution would be to reintroduce the original bug (minor memory leak 4291376) and emphasize that the caches can be flushed with existing API on the Introspector: flushCaches(), flushFromCache().

One workaround on a development system is to cache the BeanInfo classes locally so that a Strong reference is maintained. See the work around section for details.

###@###.### 2002-03-04
                                     
2002-03-04
WORK AROUND

The work around is to maintain Strong references to all the BeanInfo classes that you have set a persistence delegate.

The workaround is encapsulated in the static method setPersistenceDelegate(). When the bug is fixed in 1.4.1, all the extra BeanInfo caching may be removed.

    /**
     * Sets a persistence delgate on an encoder for a specific class.
     */
    public static void setPersistenceDelegate(Encoder encoder, Class cls, 
					      PersistenceDelegate delegate) {
	// Workaround for SoftReference BeanInfo bug.
	// Staticly hold a stong reference to the BeanInfo to prevent
	// Garbage collection due to sweep of Softly referenced BeanInfos.
	// This should be fixed for 1.4.1
	if (beanInfos == null) {
	    beanInfos = new HashMap();
	}
	BeanInfo info = (BeanInfo)beanInfos.get(cls);
	if (info == null) {
	    try {
		info = Introspector.getBeanInfo(cls);
	    } catch (Exception ex) {
		System.out.println("BeanInfo exception");
	    }
	    beanInfos.put(cls, info);
	}
        // end workaround
	
	encoder.setPersistenceDelegate(cls, delegate);
    }

If this static method and HashMap is in a class called BeanUtils, then in all  instances in which you call:

	encoder.setPersistenceDelegate(Test.class, pd);

Substitute:

	BeanUtits.setPersistenceDelegate(encoder, Test.class, pd);

When the bug is fixed, you can stub out most of the body of the static setPersistenceDelegate() method and keep the last line.

###@###.### 2002-03-04
                                     
2002-03-04
PUBLIC COMMENTS

.
                                     
2004-06-10
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
hopper

FIXED IN:
hopper

INTEGRATED IN:
hopper


                                     
2004-06-14



Hardware and Software, Engineered to Work Together