RMI服务器自行关闭

rho*_*ncu 9 java multithreading rmi

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

您可以在附加图像中看到应用程序应该完成执行的时间点作为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());
    }
}
Run Code Online (Sandbox Code Playgroud)

Stu*_*rks 9

您看到VM在其最后一个非守护程序线程退出时退出,与HotSpot垃圾收集行为,RMI的导出行为以及在NetBeans探查器下运行JVM相结合.

main()方法返回后主线程退出.但是,您已导出RMI服务器对象,因此只要存在实时的导出对象,RMI就会通过运行"RMI Reaper"线程(非守护程序)来使JVM保持活动状态.RMI通过在对象表中仅保留对它的弱引用来确定导出的对象是否"活着".

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

请注意,RMI注册表是专门处理的.导出注册表不会使JVM保持活动状态.

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

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

现在,为什么JVM会在Profiler下运行时留下来?这只是剖析器工作方式的一件神器.探查器检测到最后一个非守护程序线程已退出(代表探查器在JVM中运行的其他线程除外),因此当它弹出"已分析的​​应用程序已完成执行"的对话框时.但它会使JVM保持活动状态,因此您可以继续从中获取数据; 这有副作用使所有守护程序线程保持活动状态.您已导出注册表,以便继续侦听端口1099.当JVM处于此状态时,如果您尝试,您仍可以注册RMI对象.您的RMI服务器对象早已被取消导出和GC,因此对它的请求将不起作用.但这就是为什么当JVM处于这种状态时仍然接受RMI连接的原因.

最重要的是,确保您的RMI服务器对象没有得到GC.这样做的一个好方法是确保它们可以从静态字段中访问.