已经有很多关于此问题的问题,但是他们的答案表明导出的对象已经在服务器端进行了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关闭 - 测试。
答案 0 :(得分:-1)
NoSuchObjectException
如果尝试在远程虚拟机中不再存在的对象上调用方法,则抛出NoSuchObjectException。
这意味着您正在调用方法的存根所引用的远程对象已被取消导出,即存根是“陈旧的”。唯一可能发生的方法是手动或作为GC的结果取消导出对象。
正如所建议我使用静态引用。
不,你不是。您需要将Registry
引用设为静态。否则,您只需在Client
和Registry
之间形成一个可以一次垃圾收集的循环。
为什么你打电话给你的服务器Client
是另一个谜。
编辑一些评论:
stub = UnicastRemoteObject.exportObject(searchTestService, RMI_PORT + 1);
此处无需使用第二个端口。只需重新使用注册表端口。
log.info("Binding {} to port {}", SearchTestService.class.getName(), RMI_PORT + 1);
这是误导。您已经完成了两项工作,但您所做的是:
分两个步骤。
System.gc();
System.runFinalization();
奇怪的事情要做到这里,或者确实在任何地方。
synchronized (c) {
c.wait();
}
这不可靠。你在这里真的不需要任何东西,因为只要RMI导出远程对象,RMI应该保持JVM开放,但是你可以做注册表所做的事情:
while (true)
{
Thread.sleep(Integer.MAX_VALUE);
}
进行适当的异常处理。
我无法重现您的问题,但是我在Windows 7上。