Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
477 views
in Technique[技术] by (71.8m points)

exception - java.rmi.NoSuchObjectException: no such object in table

I am writing a very simple RMI server, and I am seeing intermittent java.rmi.NoSuchObjectExceptions in the unit tests.

I have a string of remote method calls on the same object, and while the first few go through, the later ones will sometimes fail. I am not doing anything to unregister the server object in between.

These error do not appear always, and if I put in breakpoints they tend to not appear. Are those Heisenbugs, whose race conditions dissolve when looking at them through the slowed down execution of the debugger? There is no multi-threading going on in my test or server code (though maybe inside of the RMI stack?).

I am running this on Mac OS X 10.5 (Java 1.5) through Eclipse's JUnit plugin, and the RMI server and client are both in the same JVM.

What can cause these exceptions?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Keep a strong reference to the object that implements the java.rmi.Remote interface so that it remains reachable, i.e. ineligible for garbage collection.

Below is a short program that demonstrates a java.rmi.NoSuchObjectException. The script is self-contained, creating an RMI registry as well as a "client" and a "server" in a single JVM.

Simply copy this code and save it in a file named RMITest.java. Compile and invoke with your choice of command line arguments:

  • -gc (default) Explicitly instruct the JVM to make "a best effort" to run the garbage collector after the server is started, but before the client connects to the server. This will likely cause the Remote object to be reclaimed by the garbage collector if the strong reference to the Remote object is released. A java.rmi.NoSuchObjectException is observed when the client connects after the Remote object is reclaimed.
  • -nogc Do not explicitly request garbage collection. This will likely cause the Remote object to remain accessible by the client regardless of whether a strong reference is held or released unless there is a sufficient delay between the server start and the client call such that the system "naturally" invokes the garbage collector and reclaims the Remote object.
  • -hold Retain a strong reference to the Remote object. In this case, a class variable refers to the Remote object.
  • -release (default) A strong reference to the Remote object will be released. In this case, a method variable refers to the Remote object. After the method returns, the strong reference is lost.
  • -delay<S> The number of seconds to wait between server start and the client call. Inserting a delay provides time for the garbage collector to run "naturally." This simulates a process that "works" initially, but fails after some significant time has passed. Note there is no space before the number of seconds. Example: -delay5 will make the client call 5 seconds after the server is started.

Program behavior will likely vary from machine to machine and JVM to JVM because things like System.gc() are only hints and setting the -delay<S> option is a guessing game with respect to the behavior of the garbage collector.

On my machine, after javac RMITest.java to compile, I see this behavior:

$ java RMITest -nogc -hold
received: foo
$ java RMITest -nogc -release
received: foo
$ java RMITest -gc -hold
received: foo
$ java RMITest -gc -release
Exception in thread "main" java.rmi.NoSuchObjectException: no such object in table
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:178)
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132)
    at $Proxy0.remoteOperation(Unknown Source)
    at RMITest.client(RMITest.java:69)
    at RMITest.main(RMITest.java:46)

Here is the source code:

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import static java.util.concurrent.TimeUnit.*;

interface RemoteOperations extends Remote {
    String remoteOperation() throws RemoteException;
}

public final class RMITest implements RemoteOperations {
    private static final String REMOTE_NAME = RemoteOperations.class.getName();
    private static final RemoteOperations classVariable = new RMITest();

    private static boolean holdStrongReference = false;
    private static boolean invokeGarbageCollector = true;
    private static int delay = 0;

    public static void main(final String... args) throws Exception {
        for (final String arg : args) {
            if ("-gc".equals(arg)) {
                invokeGarbageCollector = true;
            } else if ("-nogc".equals(arg)) {
                invokeGarbageCollector = false;
            } else if ("-hold".equals(arg)) {
                holdStrongReference = true;
            } else if ("-release".equals(arg)) {
                holdStrongReference = false;
            } else if (arg.startsWith("-delay")) {
                delay = Integer.parseInt(arg.substring("-delay".length()));
            } else {
                System.err.println("usage: javac RMITest.java && java RMITest [-gc] [-nogc] [-hold] [-release] [-delay<seconds>]");
                System.exit(1);
            }
        }
        server();
        if (invokeGarbageCollector) {
            System.gc();
        }
        if (delay > 0) {
            System.out.println("delaying " + delay + " seconds");
            final long milliseconds = MILLISECONDS.convert(delay, SECONDS);
            Thread.sleep(milliseconds);
        }
        client();
        System.exit(0); // stop RMI server thread
    }

    @Override
    public String remoteOperation() {
        return "foo";
    }

    private static void server() throws Exception {
        // This reference is eligible for GC after this method returns
        final RemoteOperations methodVariable = new RMITest();
        final RemoteOperations toBeStubbed = holdStrongReference ? classVariable : methodVariable;
        final Remote remote = UnicastRemoteObject.exportObject(toBeStubbed, 0);
        final Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        registry.bind(REMOTE_NAME, remote);
    }

    private static void client() throws Exception {
        final Registry registry = LocateRegistry.getRegistry();
        final Remote remote = registry.lookup(REMOTE_NAME);
        final RemoteOperations stub = RemoteOperations.class.cast(remote);
        final String message = stub.remoteOperation();
        System.out.println("received: " + message);
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...