Java DGC测试中出现意外结果-未引用()方法

Java DGC测试中出现意外结果-未引用()方法,java,rmi,Java,Rmi,我正在做一些关于JavaRMI分布式GC的测试 这是我的测试项目: 服务器端 Bootstrap.java public interface Bootstrap extends Remote { public Runnable getClient() throws RemoteException; } public class ClientBootstrap { public static void main(String[] args) { if

我正在做一些关于JavaRMI分布式GC的测试

这是我的测试项目:

服务器端

Bootstrap.java

public interface Bootstrap extends Remote
{

    public Runnable getClient() throws RemoteException;

}
public class ClientBootstrap
{

    public static void main(String[] args)
    {
        if (System.getSecurityManager() == null)
            System.setSecurityManager(new SecurityManager());

        Runnable client = null;
        try
        {
            Bootstrap bootstrapServer = (Bootstrap)
                    Naming.lookup("//" + args[0] + ":1099/BootstrapServer");
            client = bootstrapServer.getClient();           
        }
        catch (MalformedURLException | RemoteException | NotBoundException e)
        {
            e.printStackTrace();
        }

        client.run();        
    }

}
Login.java

public interface Login extends Remote
{

    public String login() throws RemoteException;

}
BootstrapServer.java

public class BootstrapServer implements Bootstrap, Unreferenced
{

    private final Login loginServerStub;

    public BootstrapServer(Login loginServerStub, int port)
            throws RemoteException
    {
        this.loginServerStub = loginServerStub;

        UnicastRemoteObject.exportObject(this, port);
    }

    @Override
    public Runnable getClient() throws RemoteException
    {
        return new Client(loginServerStub);
    }

    @Override
    public void unreferenced()
    {
        System.out.println("unreferenced() method called on BootstrapServer");
    }

}
public class LoginServer implements Login, Unreferenced
{

    public LoginServer(int port) throws RemoteException
    {
        UnicastRemoteObject.exportObject(this, port);
    }

    @Override
    public String login() throws RemoteException
    {
        return "Login successful";
    }

    @Override
    public void unreferenced()
    {
        System.out.println("unreferenced() method called on LoginServer");
    }

}
LoginServer.java

public class BootstrapServer implements Bootstrap, Unreferenced
{

    private final Login loginServerStub;

    public BootstrapServer(Login loginServerStub, int port)
            throws RemoteException
    {
        this.loginServerStub = loginServerStub;

        UnicastRemoteObject.exportObject(this, port);
    }

    @Override
    public Runnable getClient() throws RemoteException
    {
        return new Client(loginServerStub);
    }

    @Override
    public void unreferenced()
    {
        System.out.println("unreferenced() method called on BootstrapServer");
    }

}
public class LoginServer implements Login, Unreferenced
{

    public LoginServer(int port) throws RemoteException
    {
        UnicastRemoteObject.exportObject(this, port);
    }

    @Override
    public String login() throws RemoteException
    {
        return "Login successful";
    }

    @Override
    public void unreferenced()
    {
        System.out.println("unreferenced() method called on LoginServer");
    }

}
Client.java

public class Client implements Runnable, Serializable
{

    private static final long serialVersionUID = 2743368372506517302L;
    private final Login loginServerStub;


    public Client(Login loginServerStub)
    {
        this.loginServerStub = loginServerStub;
    }

    @Override
    public void run()
    {
        System.out.println("Hi! I'm the client.");
        System.out.println("This is the remote reference of the login server:");
        System.out.println(loginServerStub + "\n");
        System.out.println("I'm trying to invoke the login method:");

        try
        {
            System.out.println(loginServerStub.login());
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
        }
    }
}

Setup.java

public class Setup
{

    public static void main(String args[])
    {
        if (System.getSecurityManager() == null)
            System.setSecurityManager(new SecurityManager());

        Bootstrap bootstrapServer = null;
        try
        {
            Login loginServer = new LoginServer(Integer.parseInt(args[0]));
            bootstrapServer = new BootstrapServer(loginServer,
                    Integer.parseInt(args[1]));

            LocateRegistry.createRegistry(1099);
            Registry rmiRegistry = LocateRegistry.getRegistry();
            rmiRegistry.bind("BootstrapServer", bootstrapServer);
        }
        catch (RemoteException | AlreadyBoundException e)
        {
            e.printStackTrace();
        }       
    }

}
重要的是BootstrapServer维护LoginServer的远程引用。 当客户机调用getClient方法时,BootstrapServer将创建一个新的客户机对象。 在客户端对象中封装了LoginServer的远程引用。 最后,对客户端对象进行序列化,并将其发送到客户端应用程序

客户端

ClientBootstrap.java

public interface Bootstrap extends Remote
{

    public Runnable getClient() throws RemoteException;

}
public class ClientBootstrap
{

    public static void main(String[] args)
    {
        if (System.getSecurityManager() == null)
            System.setSecurityManager(new SecurityManager());

        Runnable client = null;
        try
        {
            Bootstrap bootstrapServer = (Bootstrap)
                    Naming.lookup("//" + args[0] + ":1099/BootstrapServer");
            client = bootstrapServer.getClient();           
        }
        catch (MalformedURLException | RemoteException | NotBoundException e)
        {
            e.printStackTrace();
        }

        client.run();        
    }

}
客户端应用程序只运行服务器发送的客户端对象

这是客户端应用程序的输出:

user@ubuntu:~/javarmi/UnreferencedTestClient$ ./client_launcher.sh 
Hi! I'm the client.
This is the remote reference of the login server:
LoginServer_Stub[UnicastRef [liveRef: [endpoint:[127.0.1.1:42000](remote),objID:[711ad51f:148ebcdecc7:-7fff, 6312833096912673336]]]]

I'm trying to invoke the login method:
Login successful
user@ubuntu:~/javarmi/UnreferencedTestClient$
一分钟后,我更改了服务器端应用程序打印的leaseValue和checkInterval参数:

unreferenced() method called on LoginServer
请注意,由于rmiregistry维护远程引用,因此从未调用BootstrapServer上的未引用方法

问题是:如果BootstrapServer获得了loginServerStub引用,为什么会在LoginServer上调用unreferenced方法

谢谢。

在服务器JVM中,BootstrapServer没有loginStub引用。它具有对实际服务器的引用。远程对象,而不是其存根。您将变量命名错误。因此,由于BootStrapServer和LoginServer之间没有远程连接,因此前者不是DGC客户机,因此后者可以获得DGC'd


客户端JVM已退出,因此任何地方都没有DGC客户端,因此,也没有什么可以阻止DGC。

BootstrapServer没有远程引用,因为该引用没有作为远程方法的参数传递吗?编辑:我尝试在BootstrapServer中添加一个远程方法,以将登录服务器的引用作为此新方法的参数传递,而不是使用构造函数。但结果是一样的,引用不是存根。我不明白为什么会将本地引用而不是存根传递给远程对象,这是文档中所说的:当在远程方法调用中将导出的远程对象作为参数或返回值传递时,而是传递该远程对象的存根。您似乎在询问客户端中发生了什么:请参阅我的第二段。您可以正确地说,远程对象在序列化时被序列化为其自己的存根,但由于客户端JVM立即退出,仍然没有幸存的DGC客户端,因此DGC可能会发生,我知道:我不清楚的是,为什么引导服务器有一个本地引用而不是远程引用。因为您存储了新登录服务器的结果。。。它是一个远程对象,而不是存根。您错误地将构造函数参数和局部变量命名为loginServerStub,但仅此一点并不能使其成为存根。我不明白你为什么不理解自己的代码。