简体   繁体   English

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

[英]java.rmi.NoSuchObjectException: no such object exception

There have been acouple of questions about this already, but their answers suggest that the exported object has been GC'd on the server side and that is cousing the problems. 已经有很多关于此的问题,但是他们的答案表明导出的对象已经在服务器端进行了GC,这就是问题所在。 However it seems like that this is not the issue here. 然而,这似乎不是问题。

Mentioned exception is thrown only on single machine: 仅在单台机器上引发异常:

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

With java: 用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)

This happend on the same machine with OpenJDK 7something as well. 这也发生在使用OpenJDK 7something的同一台机器上。

According to other answers I am supposed to keep a strong reference to handling objects. 根据其他答案,我应该对处理对象有强烈的参考。 I am doing it now, so what more can be done? 我现在正在做,那还能做些什么呢?

The same code works on windows as well as on a different remote linux machine with java 7. Any ideas why? 相同的代码适用于Windows以及使用java 7的不同远程linux机器。任何想法为什么?

I have implemented some finalizers to the connected classes, but none of them are called. 我已经为连接类实现了一些终结器,但没有一个被调用。

As suggested I am using static references. 正如建议我使用静态引用。 As for me there is no way get exported object GC enligible. 至于我,没有办法让导出的对象GC可以使用。 Exception is thrown on remote method invocation right after object lookup. 在对象查找后立即在远程方法调用上抛出异常。

Piece of class 一类

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

(...)
    }

And bootstrapping code 并引导代码

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();
        }

    }
}

and 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.

Requested piece of code - lookup and call that throws exception 请求的代码段 - 查找和调用抛出异常

//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;
}

Console output after running "listening" client side code (with RMI registry) 运行“监听”客户端代码后的控制台输出(使用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

And this waits untill manuall shutdown - tested. 这等待直到manuall关闭 - 测试。

NoSuchObjectException

Javadoc : Javadoc

A NoSuchObjectException is thrown if an attempt is made to invoke a method on an object that no longer exists in the remote virtual machine. 如果尝试在远程虚拟机中不再存在的对象上调用方法,则抛出NoSuchObjectException。

This means that the remote object referred to by the stub you are calling methods on has been unexported, ie the stub is 'stale'. 这意味着您正在调用方法的存根所引用的远程对象已被取消导出,即存根是“陈旧的”。 The only way that can happen is by unexporting the object, either manually or as a result of GC. 唯一可能发生的方法是手动或作为GC的结果取消导出对象。

As suggested I am using static references. 正如建议我使用静态引用。

No you're not. 不你不是。 You need to make the Registry reference static. 您需要使Registry引用静态。 Otherwise you just form a cycle between Client and Registry that can be garbage-collected all at once. 否则,您只需在ClientRegistry之间形成一个循环,可以一次性进行垃圾收集。

Why you're calling your server Client is another mystery. 为什么要调用服务器Client是另一个谜。

EDIT A few comments: 编辑一些评论:

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

There's no need to use a second port here. 这里没有必要使用第二个端口。 Just re-use the Registry port. 只需重新使用Registry端口即可。

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

This is misleading. 这是误导。 You've already done both, but what you have done is: 你已经完成了两件事,但你所做的是:

  1. Exported the object on the port, and 导出端口上的对象,和
  2. Bound the object to a name 将对象绑定到名称

in two separate steps. 在两个单独的步骤。

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

Strange things to be doing here, or indeed anywhere. 奇怪的事情要做到这里,或者确实在任何地方。

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

This isn't reliable. 这不可靠。 You don't really need anything here, as RMI should keep the JVM open as long as it has exported remote objects, but you could do what the Registry does: 这里你真的不需要任何东西,因为RMI应该保持JVM打开,只要它已经导出远程对象,但是你可以做注册表所做的事情:

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

with the appropriate exception handling. 与适当的异常处理。

I can't reproduce your problem, but then I'm on Windows 7. 我无法重现你的问题,但后来我在Windows 7上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM