【问题标题】:java.rmi.NoSuchObjectException: no such object exceptionjava.rmi.NoSuchObjectException:没有这样的对象异常
【发布时间】:2015-08-16 02:36:49
【问题描述】:

已经有一些关于这个的问题,但他们的回答表明导出的对象已经在服务器端进行了 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 7 相同的机器上。

根据其他答案,我应该对处理对象保持强烈的参考。我现在正在做,还有什么可以做的?

相同的代码可以在 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();
        }

    }
}

和堆栈跟踪

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

这会等到手动关机 - 测试过。

【问题讨论】:

  • 您的服务器是否从数据库中获取某些内容并将其返回给客户端?我认为这可能是错误的意思是“表中没有这样的对象”
  • @BilboBaggins 不,这里没有 DAO
  • @shekharsuman SearchTestService.class 是一个interface extends Remote。我将在帖子中添加查找代码,但请记住,它在 4 台计算机上运行良好,但在其中一台计算机上抛出异常。
  • @Antoniossss-你能分享一下你的远程接口的代码SearchTestService吗?而且,正如 EJP 所说,在您的类 Client 中将 private Registry registry; 设为静态!另外,请在 SearchTestService c = getClient(address);! 中显示您作为 address 传递的内容
  • 地址是一个 ip:port 字符串。明天贴界面代码。但是我不认为这是相关的。再次。此代码适用于本地和网络上的多台机器。但是仅在其中一个上引发异常

标签: java rmi


【解决方案1】:
NoSuchObjectException

Javadoc:

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

这意味着您正在调用方法的存根引用的远程对象尚未导出,即存根是“陈旧的”。可能发生的唯一方法是手动或通过 GC 取消导出对象。

正如建议的那样,我正在使用静态引用。

不,你不是。您需要将 Registry 引用设为静态。否则,您只会在 ClientRegistry 之间形成一个循环,可以一次将其全部回收。

为什么你调用你的服务器Client 是另一个谜。

编辑几个cmets:

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

这里不需要使用第二个端口。只需重新使用注册表端口即可。

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

这是误导。你已经做了这两件事,但你所做的是:

  1. 在端口上导出对象,并且
  2. 将对象绑定到名称

分两个单独的步骤。

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

在这里或任何地方都可以做一些奇怪的事情。

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

这不可靠。您实际上不需要任何东西,因为只要 JVM 导出了远程对象,RMI 就应该保持 JVM 处于打开状态,但是您可以执行 Registry 所做的事情:

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

使用适当的异常处理。

我无法重现您的问题,但我使用的是 Windows 7。

【讨论】:

  • 至于奥秘,这段代码将在多台机器上同时启动,控制器将远程调用客户端;)如果注册表是 GCd,我将无法查找为我现在可以吗?此外,这是我第一次必须执行这样的恶作剧,并且我已经在 RMI 应用程序上做了十几个。
  • 还有一件事,由于对 Client 对象的强引用并在其上持有监视器,因此这里的所有字段都不是可垃圾收集的。
  • 而且仅供参考,对registry 的静态引用并没有改变任何东西。
  • @shekharsuman 您是否有疑问,或者您认为这是原因?无论如何-缺少包=没有编译的jar。这里根本不使用反射。
  • @downvoters 如果您对 RMI 有所了解,但我不知道,我很想听听。无法解释的投票只是网站破坏行为。
猜你喜欢
  • 2010-10-13
  • 2019-02-09
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多