java - RMI 服务器自行关闭

标签 java multithreading rmi

我正在编写一个使用 RMI 进行通信的 Java 客户端-服务器应用程序。我的问题是,出于某种原因,RMI 服务器自行关闭,没有任何异常或错误。我正在使用 Netbeans,我运行了一个配置文件来查看线程。 Server Threads

您可以在附图中看到应用程序应该在 GC 守护进程和 RMI Reaper 线程结束时完成执行的时间点。然而,即使在应用程序结束后,RMI TCP Accept-1099 线程仍在运行。更让我困惑的是,在信息消息弹出后(你可以在屏幕截图中看到)告诉我服务器已停止,图中的线程继续更新,所以我再次尝试与客户端连接.虽然它失败了,但我可以看到正在创建一个新的 RMI 线程(连接 18)。

我不知道如何调试这个问题,我无法弄清楚当 RMI 接受线程仍在运行时应用程序如何退出。

更新:这是服务器的主要方法:

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

最佳答案

您会看到 VM 在其最后一个非守护线程退出时退出的交互,结合 HotSpot 垃圾收集行为、RMI 的导出行为,以及在 NetBeans 分析器下运行 JVM。

main() 方法返回后主线程退出。但是,您导出了一个 RMI 服务器对象,因此只要存在 Activity 的导出对象,RMI 就会通过运行“RMI Reaper”线程(非守护进程)来保持 JVM Activity 。 RMI 通过在对象表中仅保留对导出对象的弱引用来确定导出对象是否“存活”。

不幸的是,通过查看 main() 方法,您的 RMI 服务器对象似乎仅通过局部变量引用。因此,它迟早会被垃圾回收,但由于 RMI 对象表中对它的弱引用。当对象变得弱可达时,RMI Reaper 取消导出它并退出。由于 RMI Reaper 是最后一个非守护线程,因此 JVM 退出。

请注意,RMI 注册表是经过特殊处理的。导出注册表不会使 JVM 保持 Activity 状态。

另请注意,在 main() 方法的末尾放置一个无限循环将不一定阻止 RMI 服务器对象被取消导出和 GC。原因是对象在变得无法访问 时会受到 GC 的影响,并且 Activity 方法的局部变量中存在的引用不足以使其可访问。参见 my answer关于该主题的另一个问题。当然,将无限循环放入 main() 将阻止 JVM 退出,因为它使主线程保持 Activity 状态,而主线程不是守护线程。

为了防止您的 RMI 服务器被取消导出,通常在静态字段中存储对它的引用就足够了。

现在,为什么 JVM 在分析器下运行时会一直存在?这只是探查器工作方式的产物。探查器检测到最后一个非守护进程线程已经退出(除了代表探查器在 JVM 中运行的其他线程),所以这时它会弹出对话框,显示“已探查的应用程序已完成执行”。不过,它使 JVM 保持 Activity 状态,因此您可以继续从中获取数据;这具有使所有守护线程保持 Activity 状态的副作用。您已经导出注册表,因此它会一直监听端口 1099。当 JVM 处于这种状态时,如果您尝试过,您可能仍然能够注册 RMI 对象。您的 RMI 服务器对象早已未导出和 GC,因此对它的请求将不起作用。但这就是为什么当 JVM 处于这种状态时 RMI 连接仍然被接受。

底线是,确保您的 RMI 服务器对象不会被 GC 处理。执行此操作的一个好方法是确保可以从静态字段访问它们。

关于java - RMI 服务器自行关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25872985/

相关文章:

java - 如何隐藏底部导航栏?

java - 在 Java 中,什么时候应该在接口(interface)中使用抽象方法?

java - JVM/CLR 上相同字节的不同浮点值

java - java中使用hashmap的有向图

java - 更改方法的参数

java - 为什么客户端线程只执行一次就销毁了? Java TCP 套接字

java - Java分布式程序的当前最佳实践

java - 运行页面刷新后发生的线程(失控线程)

spring - 集成基于 Spring 的 Java 应用程序和 Clojure 库

java - 如何使用 Java RMI 从服务器向客户端发送消息?