当前位置:  开发笔记 > 编程语言 > 正文

java.rmi.NoSuchObjectException:表中没有这样的对象

如何解决《java.rmi.NoSuchObjectException:表中没有这样的对象》经验,为你挑选了2个好方法。

我正在编写一个非常简单的RMI服务器,我java.rmi.NoSuchObjectExceptions在单元测试中看到间歇性的.

我在同一个对象上有一串远程方法调用,而前几个调用完成后,后面的调用有时会失败.我没有做任何事情来取消注册服务器对象.

这些错误并不总是出现,如果我输入断点,它们往往不会出现.是那些Heisenbugs,当通过调试器的执行速度减慢来查看它们时,它们的竞争条件会消失吗?我的测试或服务器代码中没有多线程(尽管可能在RMI堆栈内部?).

我通过Eclipse的JUnit插件在Mac OS X 10.5(Java 1.5)上运行它,并且RMI服务器和客户端都在同一个JVM中.

什么可以导致这些例外?



1> Greg Mattes..:

保持对实现java.rmi.Remote接口的对象的强引用,以使其保持可访问状态,即不符合垃圾回收的条件.

下面是一个简短的程序,演示了一个java.rmi.NoSuchObjectException.该脚本是自包含的,在单个JVM中创建RMI注册表以及"客户端"和"服务器".

只需复制此代码并将其保存在名为的文件中即可RMITest.java.使用您选择的命令行参数进行编译和调用:

-gc(默认)显式指示JVM在服务器启动后但在客户端连接到服务器之前,"尽最大努力"运行垃圾收集器.如果释放对该对象的强引用,这可能会导致该Remote对象被垃圾回收器回收.在回收对象后客户端连接时观察到A.Remotejava.rmi.NoSuchObjectExceptionRemote

-nogc不要显式请求垃圾回收.这可能会导致该Remote对象保持客户端访问无论很强的借鉴意义是否滞留或释放,除非有足够的延迟服务器启动和客户端调用之间,使得系统的"自然"调用垃圾收集和回收的Remote对象.

-hold保留对该Remote对象的强引用.在这种情况下,类变量引用该Remote对象.

-release(默认)Remote将释放对该对象的强引用.在这种情况下,方法变量引用该Remote对象.方法返回后,强引用将丢失.

-delay服务器启动和客户端调用之间等待的秒数.插入延迟为垃圾收集器"自然地"运行提供了时间.这模拟了一个最初"有效"的过程,但在经过一段时间后失败了.请注意,秒数之前没有空格.示例:-delay5将在服务器启动后5秒进行客户端调用.

程序行为可能因机器和JVM到JVM而异,因为类似System.gc()的东西只是提示并设置-delay选项是关于垃圾收集器行为的猜谜游戏.

在我的机器上,javac RMITest.java编译之后,我看到了这种行为:

$ 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)

这是源代码:

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]");
                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);
    }
}


答对了.保持参考的责任似乎落在程序员身上.在绑定生效时,我对RMI系统保持引用抱有同样的希望.它可能是一个很好的理由(我没有研究过RMI来源).无论如何,它似乎并不过分直观.
这个答案是不正确的.它完全忽略了DGC的影响.远程对象的DGC客户端的存在足以防止它在本地GC.在这种情况下,注册表通过为其提供远程存根而成为远程对象的DGC客户端.这个代码行为的原因是因为*Registry*本身得到了GC'd,它既不会导出它又会释放它的所有绑定,而这些绑定又会释放它对远程对象的DGC保留,从而允许远程对象在本地GC.
我希望在我"解除"对象之前,RMI系统会让它保持活力.

2> jottos..:

还需要考虑的其他一些问题 - 首先是您引用对象实例还是存根接口本身已经消失了?如果某个对象实例消失了,由于通常的原因,它被取消引用并且GC'd,但是如果它是接口,则由于某种原因你的RMI服务器端点循环退出.

到目前为止,我发现的最好的调试工具是打开java.rmi.server.logCalls = true属性(请参阅http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties .html)并在日志窗口中查看所有精彩信息流.这告诉我每次都是什么.

乔斯

推荐阅读
手机用户2402851335
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有