Java RMI服务器自行关闭
我正在编写一个Java客户机-服务器应用程序,它使用RMI进行通信。我的问题是,出于某种原因,RMI服务器只是自动关闭,没有异常或错误。我正在使用Netbeans,我运行了一个配置文件来查看线程。 您可以在所附的映像中看到应用程序作为GC守护进程和RMI Reaper线程的末尾完成执行的时间点。但是,即使在应用程序结束后,RMI TCP Accept-1099线程仍在运行。更让我困惑的是,在弹出信息消息(您可以在屏幕截图中看到)告诉我服务器已停止后,图中的线程继续更新,因此我再次尝试与客户端连接。虽然失败了,但我可以看到正在创建一个新的RMI线程(连接18) 我不知道如何调试这个问题,也不知道当RMI accept线程仍在运行时应用程序如何退出 更新:以下是服务器的主要方法:Java RMI服务器自行关闭,java,multithreading,rmi,Java,Multithreading,Rmi,我正在编写一个Java客户机-服务器应用程序,它使用RMI进行通信。我的问题是,出于某种原因,RMI服务器只是自动关闭,没有异常或错误。我正在使用Netbeans,我运行了一个配置文件来查看线程。 您可以在所附的映像中看到应用程序作为GC守护进程和RMI Reaper线程的末尾完成执行的时间点。但是,即使在应用程序结束后,RMI TCP Accept-1099线程仍在运行。更让我困惑的是,在弹出信息消息(您可以在屏幕截图中看到)告诉我服务器已停止后,图中的线程继续更新,因此我再次尝试与客户端连
/**
* Main entry point.
*
* @param args the application arguments - unused.
*/
public static void main(String[] args) {
try {
System.setProperty("java.rmi.dgc.leaseValue", "30000");
sServerProperties = new ServerProperties();
System.setProperty("java.rmi.server.hostname", sServerProperties.
getRmiServer());
createRmiRegistry();
ConfigCore configCore = new ConfigCore();
ServerCore server = new ServerCore(configCore);
LoginHandler loginHandler = new LoginHandler(server);
sRegistry.
bind(Login.class.getSimpleName(), loginHandler.getRemote());
Logger.log(Level.INFO, "Server ready!");
} catch (RemoteException ex) {
Logger.log(Level.SEVERE, "Unable to start RMI registry", ex);
} catch (SQLException ex) {
Logger.log(Level.SEVERE, "Unable to connect to the MySQL server",
ex);
System.err.println(ex.getMessage());
} catch (IOException ex) {
Logger.log(Level.SEVERE, "Unable to load or write properties file",
ex);
System.err.println(ex.getMessage());
} catch (AlreadyBoundException ex) {
Logger.log(Level.SEVERE, "RMI port already bounded", ex);
} catch (NoSuchAlgorithmException ex) {
Logger.log(Level.SEVERE, "Unable to digest password", ex);
}
}
/**
* Creates the RMI registry.
*
* @throws RemoteException if the RMI registry could not be created.
*/
private static void createRmiRegistry() throws RemoteException {
if (sRegistry == null) {
Logger.log(Level.INFO, "Creating RMI Registry...");
sRegistry = LocateRegistry.createRegistry(sServerProperties.
getRmiPort());
}
}
这是RMI的常见问题。您需要将对实现的引用保存为类字段,这样它就不会得到GC。您还需要保持该类(带有main())的活动状态 我用这句话作为永无止境的口号:
for(;;) LockSupport.park();
您可以做任何您想做的事情,这样实现保持活动状态,而main()类保持活动状态。您将看到VM在其最后一个非守护进程线程退出时退出的交互,以及热点垃圾收集行为、RMI的导出行为和在NetBeans探查器下运行JVM 主线程在
main()
方法返回后退出。但是,您已经导出了一个RMI服务器对象,因此只要存在活动的导出对象,RMI就会通过运行“RMI收割机”线程(非守护进程)使JVM保持活动状态。RMI通过在it对象表中仅保留对导出对象的弱引用来确定导出对象是否“活动”
不幸的是,通过查看main()
方法,您的RMI服务器对象似乎仅通过局部变量引用。因此,它迟早会被垃圾收集,但在RMI的对象表中对它的引用较弱。当对象变得弱可及时,RMI收割机将其取消导出并退出。因为RMI收割机是最后一个非守护进程线程,所以JVM退出
请注意,RMI注册表是经过特殊处理的。导出注册表不会使JVM保持活动状态
还要注意的是,在main()
方法的末尾放置一个无限循环将不一定会阻止RMI服务器对象未报告和GC。原因是,当对象变得不可访问时,对象会受到GC的约束,而活动方法的局部变量中存在的引用不足以使其可访问。看看关于那个话题的另一个问题。当然,将无限循环放入main()
将阻止JVM退出,因为它使主线程保持活动状态,并且主线程不是守护线程
为了防止RMI服务器未被报告,通常在静态字段中存储对它的引用就足够了
现在,为什么JVM在探查器下运行时仍然存在?这只是分析器工作方式的产物。探查器检测到最后一个非守护进程线程已退出(而不是代表探查器在JVM中运行的其他线程),因此,此时它会弹出一个对话框,显示“探查的应用程序已完成执行”。不过,它会使JVM保持活动状态,以便您可以继续从中获取数据;这会产生副作用,使所有守护进程线程保持活动状态。您已经导出了一个注册表,因此它将继续侦听端口1099。当JVM处于这种状态时,如果您尝试,您可能仍然能够注册RMI对象。您的RMI服务器对象很久以来一直未被报告和GC'd,因此对它的请求将无法工作。但这就是为什么在JVM处于这种状态时仍然接受RMI连接的原因
底线是,确保您的RMI服务器对象没有得到GC'd。这样做的一个好方法是确保可以从静态字段访问它们。您的应用程序是否处于无限循环中?如何确保
main
线程未完成?导出并绑定RMI存根后,主线程立即完成。应用程序只应在关闭所有线程后完成,而不仅仅是关闭主线程。主线程是线程图中的第一个条形图,您可以看到它立即关闭。所有非守护进程线程。好的,那么为什么应用程序在主线程完成后不立即关闭?@EJP至少在JDK 8中,RMI accept线程是守护进程线程。这在过去可能有所不同,但它们已经是守护进程线程很长一段时间了。他需要将注册表对象保存为静态对象!为了防止它被GC攻击,他似乎正在这么做。我正在保存所有导出的引用。我没有意识到我需要保持主要的运行。事实上,我曾经在绑定登录存根后做过Main.class.wait(),但仍然没有用,服务器一直在随机关闭。@EJP我做RMI的时间没有你长,但16年就快到了。和往常一样,很高兴再次与你交谈。我以前也遇到过同样的问题,我所做的,如上所述,是为了让服务器保持活力。我确实保留了一个对存根的引用,但这是一个不需要思考的问题。OP wait()可能会出现虚假唤醒,因此请使用park()。如果这不能解决您的问题,那么您还有另一个问题。它可以使服务器保持活动状态,但没有必要使服务器保持活动状态。他不需要这么做,等等。(1) 注册处没有得到特别处理。如果不静态存储,它可以像其他任何东西一样获得GCd。(2) OP的远程对象绑定在注册表中,这会导致DGC租赁,这会导致强本地引用,这会导致