RMI服务器自行关闭

时间:2014-09-16 15:37:44

标签: 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());
    }
}

2 个答案:

答案 0 :(得分:9)

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

主要线程在main()方法返回后退出。但是,您已导出RMI服务器对象,因此RMI通过运行" RMI Reaper"来保持JVM活跃。线程(非守护进程)只要有实时的导出对象。 RMI确定导出的对象是否为" alive"通过在对象表中仅保留对它的弱引用。

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

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

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

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

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

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

答案 1 :(得分:-2)

这是RMI的常见问题。您需要将对实现的引用保存为类字段,以便它不会获得GC。你需要保持那个类(main())还活着。

我用这个作为一个永无止境的活着:

for(;;) LockSupport.park(); 

你可以做任何你喜欢的事情,因此实现仍然存在,并且main()的类保持活动状态。