Name: boT120536 Date: 07/31/2001
java version "1.2.2" (and 1.3.1, per user)
Classic VM (build JDK-1.2.2_006, native threads, symcjit)
When an RMI client application gets a reference to a server object that uses a
custom client socket factory, the factory gets serialized and instantiated on
the client JVM. When references to the remote object are released, the object
implementing RMIClientSocketFactory is not released and garbage collected.
Working with some tools I was able to determine that there are
sun.rmi.transport.tcp.TCPEndpoint objects holding references to the client
socket factory. Furthermore, there seems to be circular references between
these objects and TCPTransport and TCPChannel objects (all in the
sun.rmi.transport.tcp package).
This problem does not seem to happen when using the standard rmi socket
factories, but apparently (per the RMI documentation) the endpoint objects and
data are handled differently when using custom factories.
This problem is also present in JDK 1.3.1.
STEPS TO REPRODUCE:
- Create an RMI server object (factory) that will create instances of another
server object on demand (a session object). Have the session object use custom
client and server socket factories.
- Create a client that looks up the factory object and then loops to request
session objects, make a simple call to them and then releases them. Using a
memory tool (I'm using OptimizeIT), you can see that instances of the socket
factories are created on the client that are then not released when the session
objects are released. Using a debugger that will show threads, it seems like
there are two threads that are created for every remote reference that have
something to do with the client socket factory. One of the threads seems to be
finishing and getting collected when the reference is lost. The other thread,
however, seems to stay around indefinitely.
SOURCE CODE:
There are two packages, client and server:
The client package contains a single class that finds the registry, gets a
reference to the factory object, and then calls the factory in a loop to create
a number of session objects. After the loop is done, it calls the garbage
collection, sleeps for some time (30s), calls garbage collection again and
sleeps for some more time (60s). I was trying to get force garbage
collection. The second sleep call is to have time to look at threads and
objects using a debugger or an object inspector. These sleep times can of
course be changed to higher values.
The server factory has the factory object and interface, the session object and
interface and the socket factories, client and server. I'm not including the
code for the stubs as this can be generated using rmic.
Following is the source code for the client:
package client;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import server.*;
public class TestClient
{
public static void main(String[] args)
{
try
{
Registry registry = LocateRegistry.getRegistry ();
IFactory factory = (IFactory)registry.lookup ("factory");
for (int count = 0; count < 50; ++count)
{
ISession session = factory.getSession();
session.dummyCall();
}
System.gc();
System.out.println ("client sleeping...");
Thread.currentThread().sleep (30000);
System.gc ();
System.out.println ("client sleeping...");
Thread.currentThread().sleep (60000);
}
catch (Throwable t)
{
t.printStackTrace ();
}
}
}
Server source code:
package server;
import java.rmi.server.*;
import java.net.*;
import java.io.*;
public class ClientSocketFactory implements RMIClientSocketFactory, Serializable
{
public Socket createSocket(String host, int port) throws IOException
{
return new Socket(host, port);
}
}
package server;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
public class Factory extends UnicastRemoteObject implements IFactory
{
RMIClientSocketFactory m_ClientSocketFactory;
RMIServerSocketFactory m_ServerSocketFactory;
protected Factory() throws RemoteException
{
super();
m_ClientSocketFactory = new ClientSocketFactory ();
m_ServerSocketFactory = new ServerSocketFactory ();
}
public ISession getSession() throws RemoteException
{
return new Session (1100, m_ClientSocketFactory, m_ServerSocketFactory);
}
public static void main(String[] args)
{
try
{
// Create a registry
Registry registry = LocateRegistry.createRegistry(1099);
// Create and bind the factory
Factory factory = new Factory ();
registry.rebind ("factory", factory);
System.out.println ("Server is ready.");
}
catch (Throwable t)
{
t.printStackTrace ();
}
}
}
package server;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
public interface IFactory extends Remote
{
public ISession getSession() throws RemoteException;
}
package server;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
public interface ISession extends Remote
{
public void dummyCall() throws RemoteException;
}
package server;
import java.io.*;
import java.rmi.server.*;
import java.net.*;
public class ServerSocketFactory implements RMIServerSocketFactory, Serializable
{
public ServerSocket createServerSocket(int port) throws IOException
{
return new ServerSocket(port);
}
}
package server;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
public class Session extends UnicastRemoteObject implements ISession
{
public Session(int port, java.rmi.server.RMIClientSocketFactory csf,
java.rmi.server.RMIServerSocketFactory ssf) throws java.rmi.RemoteException {
super(port, csf, ssf);
}
public void dummyCall() throws RemoteException
{
}
}
(Review ID: 128716)
======================================================================