java.rmi.NoSuchObjectException:没有这样的对象异常

时间:2015-06-02 10:38:57

标签: java rmi

已经有很多关于此问题的问题,但是他们的答案表明导出的对象已经在服务器端进行了GC并且存在问题。但是,这似乎不是问题所在。

仅在单台计算机上引发异议:

PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"

使用java:

java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

这也发生在使用OpenJDK 7something的同一台机器上。

根据其他答案,我应该对处理对象有强烈的参考。我现在正在做,那还能做些什么呢?

相同的代码适用于Windows以及使用java 7的不同远程linux机器。 有什么想法吗?

我已经为连接类实现了一些终结器,但没有一个被调用。

正如所建议我使用静态引用。至于我,没有办法让导出的对象GC可以使用。 在对象查找后立即在远程方法调用上抛出异常。

一类

public class Client{
     //some fields
    private final int RMI_PORT;
    private static SearchTestServiceImpl searchTestService;
    private static Remote stub;
    private Registry registry;
    //and starting service
    public void startService() throws RemoteException {
        createRegistry();
        searchTestService = new SearchTestServiceImpl(name);
        stub = UnicastRemoteObject.exportObject(searchTestService, RMI_PORT + 1);
        registry.rebind(SearchTestService.class.getName(), stub);
        log.info("Binding {} to port {}", SearchTestService.class.getName(), RMI_PORT + 1);
    }

    private void createRegistry() throws RemoteException {
        log.info("Starting RMI registry on port {}", RMI_PORT);
        registry = LocateRegistry.createRegistry(RMI_PORT);
        }

(...)
    }

引导代码

public class Bootstrap {

    private Logger log = LoggerFactory.getLogger(Bootstrap.class);
    private static Client c;

    public static void main(String[] args) throws NumberFormatException, 
// some preparations
        c = new Client(Integer.valueOf(port), name);
        c.startService();
        System.gc();
        System.runFinalization();
        synchronized (c) {
            c.wait();
        }

    }
}

和stacktrace

java.rmi.NoSuchObjectException: no such object in table
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source) ~[na:1.7.0_65]
    at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source) ~[na:1.7.0_65]
    at sun.rmi.server.UnicastRef.invoke(Unknown Source) ~[na:1.7.0_65]
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source) ~[na:1.7.0_65]
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source) ~[na:1.7.0_65]
    at com.sun.proxy.$Proxy0.getName(Unknown Source) ~[na:na]
    at call to a method of remote lookedup object #getName in this example.

请求的代码段 - 查找和调用抛出异常

//somewhere

SearchTestService c = getClient(address); // this returns nice stub
String name = c.getName(); // this is throwing exception

private SearchTestService getClient(String string) throws NumberFormatException, RemoteException, NotBoundException {
    String[] parts = string.split(":");
    Registry registry = LocateRegistry.getRegistry(parts[0], Integer.parseInt(parts[1]));
    SearchTestService client = (SearchTestService) registry.lookup(SearchTestService.class.getName());
    return (SearchTestService) client;
}

运行“侦听”客户端代码(使用RMI注册表)后的控制台输出

10:17:55.915 [main] INFO  pl.breeze.searchtest.client.Client - Starting RMI registry on port 12097
10:17:55.936 [main] INFO  p.b.s.client.SearchTestServiceImpl - Test agent Breeze Dev staging is up and running
10:17:55.952 [main] INFO  pl.breeze.searchtest.client.Client - Binding pl.choina.searchtest.remote.SearchTestService to port 12098

这等待直到manuall关闭 - 测试。

1 个答案:

答案 0 :(得分:-1)

NoSuchObjectException

Javadoc

  

如果尝试在远程虚拟机中不再存在的对象上调用方法,则抛出NoSuchObjectException。

这意味着您正在调用方法的存根所引用的远程对象已被取消导出,即存根是“陈旧的”。唯一可能发生的方法是手动或作为GC的结果取消导出对象。

  

正如所建议我使用静态引用。

不,你不是。您需要将Registry引用设为静态。否则,您只需在ClientRegistry之间形成一个可以一次垃圾收集的循环。

为什么你打电话给你的服务器Client是另一个谜。

编辑一些评论:

stub = UnicastRemoteObject.exportObject(searchTestService, RMI_PORT + 1);

此处无需使用第二个端口。只需重新使用注册表端口。

log.info("Binding {} to port {}", SearchTestService.class.getName(), RMI_PORT + 1);

这是误导。您已经完成了两项工作,但您所做的是:

  1. 导出端口上的对象,
  2. 将对象绑定到名称
  3. 分两个步骤。

    System.gc();
    System.runFinalization();
    

    奇怪的事情要做到这里,或者确实在任何地方。

    synchronized (c) {
        c.wait();
    }
    

    这不可靠。你在这里真的不需要任何东西,因为只要RMI导出远程对象,RMI应该保持JVM开放,但是你可以做注册表所做的事情:

    while (true)
    {
        Thread.sleep(Integer.MAX_VALUE);
    }
    

    进行适当的异常处理。

    我无法重现您的问题,但是我在Windows 7上。