JDK-6893109 : memory leak in readObject() and writeObject() using idlj from jdk 1.6.0_14
  • Type: Bug
  • Component: other-libs
  • Sub-Component: corba:orb
  • Affected Version: 6u14
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2009-10-19
  • Updated: 2011-02-16
  • Resolved: 2009-11-30
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 JDK 7
5.0u23-revFixed 6u17-rev b08Fixed 7Fixed
Description
Customer submitted the following testcase.

$ more Example.idl
interface Example 
{
};

and compiled it with.

$ idlj -verbose Example.idl 
Parsing Example.idl
done  - Example.idl

Generating Example
done   -   Example

created the following files

$ ls
ExampleHelper.java      Example.idl             ExampleOperations.java
ExampleHolder.java      Example.java            _ExampleStub.java

The customer explains the issue is with _ExampleStub.java

$ more _ExampleStub.java

/**
* _ExampleStub.java .
* Generated by the IDL-to-Java compiler (portable), version "3.2"
* from Example.idl
* Monday, October 19, 2009 3:13:55 PM PDT
*/

public class _ExampleStub extends org.omg.CORBA.portable.ObjectImpl implements Example
{

  // Type-specific CORBA::Object operations
  private static String[] __ids = {
    "IDL:Example:1.0"};

  public String[] _ids ()
  {
    return (String[])__ids.clone ();
  }

  private void readObject (java.io.ObjectInputStream s) throws java.io.IOException
  {
     String str = s.readUTF ();
     String[] args = null;
     java.util.Properties props = null;
     org.omg.CORBA.Object obj = org.omg.CORBA.ORB.init (args, props).string_to_object (str);
     org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
     _set_delegate (delegate);
  }

  private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException
  {
     String[] args = null;
     java.util.Properties props = null;
     String str = org.omg.CORBA.ORB.init (args, props).object_to_string (this);
     s.writeUTF (str);
  }
} // class _ExampleStub

The readObject() generates an ORB which stays in the heap. It is not gc'd. This causes the OOM error in the customer's program. The customer says a similar problem exists with writeObject(). They explain that the problem with these methods is that they cause an ORB to be created. In some cases they created a threadpool and even had threads running. In customer's case these threads prevented GC of his classLoader. He strongly feels serialization should not be creating ORBs. If it is required that an ORB be created, they should be completely destroyed and ensure that all threads have exited.

Comments
EVALUATION http://hg.openjdk.java.net/jdk7/build/corba/rev/459c07278c3c
25-12-2010

EVALUATION http://hg.openjdk.java.net/jdk7/tl/corba/rev/2ded3bb14529
08-11-2010

EVALUATION Here is an evaluation by Ken Cavanaugh. I haven't looked at this part of the code in probably 10 years. Taking a quick look this morning, I think the customer is correct. The idlj-generated serialization code is extremely bad, both because it does not destroy the ORB it creates, and it does not do anything to cache the ORB (creating an ORB can be quite expensive). In fact, I agree with the customer that no ORB should be created for this. However, we do have a problem: it appears that there is no standard, portable serialization format or mechanism defined for IDL generated stubs. Literally all the the spec says is the following (taken from http://www.omg.org/spec/I2JAV/1.3/PDF section 4.4.1.3): Those generated classes that are not abstract, including the stub classes, shall support Java object serialization semantics. For example, generated helper classes do not have to be serializable. The following classes support Java object serialization semantics: * Stub classes * Abstract base classes for concrete valuetypes * Implementation classes for concrete valuetypes * Any class that implements IDLEntity That specification is not implementable in any reasonable fashion that is standard and portable. The reverse mapping (Java to IDL, with stubs generated by rmic -iiop) DOES carefully define a serialization format for stubs, and it can be implemented portably without needing to create an ORB. The serialization format is defined in the OMG specification http://www.omg.org/spec/JAV2I/1.4/PDF in section 4.5.1.2 as follows: The writeObject and readObject methods support stub serialization and deserialization by saving and restoring the IOR associated with the stub. The writeObject method writes the following data to the serialization stream: 1. int - length of IOR type id 2. byte[] - IOR type ID encoded using ISO 8859-1 (written using a write call, not a writeObject call) 3. int - number of IOR profiles 4. For each IOR profile: 1. int - profile tag 2. int - length of profile data 3. byte[] - profile data (written using a write call, not a writeObject call). We should probably look at doing something similar in the IDL case. However, this is NOT compatible with the format used today in the current IDLJ-generated stubs, and creating a library to do the current string_to_object/object_to_string equivalent would create some other problems. Any solution here is going to lack both portability (because we cannot add new classes to the org.omg.* package, therefore no one else's ORB can assume that it can access any new code) and interoperability (because there is no real standard for IDL stub serialization). The current implementation of string_to_object and object_to_string uses a lot of ORB machinery (notably the CDR streams) to implement the functionality of converting to a string. One simple thing that could be done here is to fix the current bad code, e.g. replace private void readObject (java.io.ObjectInputStream s) throws java.io.IOException { String str = s.readUTF (); String[] args = null; java.util.Properties props = null; org.omg.CORBA.Object obj = org.omg.CORBA.ORB.init (args, props).string_to_object (str); org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate (); _set_delegate (delegate); } with private void readObject( java.io.ObjectInputStream s ) throws java.io.IOException { String str = s.readUTF() ; String[] args = null ; java.util.Properties = null ; org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init( args, props ) ; try { org.omg.CORBA.Object obj = orb.string_to_object( str ) ; org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate() ; _set_delegate( delegate ) ; } finally { orb.destroy() ; } } and similarly for writeObject. This simply fixes the worst of the problems with the current code, but the customer may still have performance issues if they do this sort of operation frequently. Caching is possible, with a static data member in the stub that has a weak reference to an ORB, so that the ORB can be GCed when needed (weak references did not exist in Java when this part of idlj was written). Sustaining should be able to handle this sort of change, if the customer has a support contract so that they can open an escalation.
19-10-2009