JDK-4646747 : Wrong getPersistenceDelegate() return after memory stress
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-03-04
  • Updated: 2002-04-27
  • Resolved: 2002-04-27
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
1.4.1 hopperFixed
Related Reports
Relates :  
Relates :  
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
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: hopper FIXED IN: hopper INTEGRATED IN: hopper
14-06-2004

PUBLIC COMMENTS .
10-06-2004

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
04-03-2002

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
04-03-2002