WeakReference到垃圾收集远程对象

时间:2015-08-12 12:07:12

标签: java garbage-collection jgroups

我正在使用JGroups作为分布式系统。我想在远程JVM上创建对象并使用它们就像在本地创建它们一样。因此,我使用java.lang.reflect.Proxy来包装RPC调用。这很像RMI行为。这非常有效。

但是现在我想在客户端接口/代理不再使用的情况下垃圾收集远程对象。所以我认为我可以通过使用WeakReference来实现这一点,但我从未陷入GC循环。我错过了什么?

public class RMILikeWrapper {
    private static final ScheduledExecutorService garbageCollector = Executors.newSingleThreadScheduledExecutor();
    private final Map remoteObjects = new ConcurrentHashMap();
    private final Map<WeakReference, IdPointer> grabageTracker = new ConcurrentHashMap<>();
    private final ReferenceQueue rq = new ReferenceQueue();
    private final RpcDispatcher rpcDispatcher;
    private final long callTimeout = 10000L;

    private class IdPointer {
        public final Address address;
        public final String id;

        public IdPointer(Address address, String id) {
            this.address = address;
            this.id = id;
        }

        @Override
        public String toString() {
            return "IdPointer{" + "address=" + address + ", id='" + id + '\'' + '}';
        }
    }

    public RMILikeWrapper(Channel channel) {
        this.rpcDispatcher = new RpcDispatcher(channel, null, null, this);

        // enable garbage collecting
        garbageCollector.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("my GC ");

                Reference<?> ref;  //this should be our weak reference
                while((ref = rq.poll()) != null) {
                    // remove weak reference from the map
                    IdPointer garbage = grabageTracker.remove(ref);

                    System.out.println("found expired weak references: " + garbage);
                    // now we need to destroy the remote object too
                    try {
                        rpcDispatcher.callRemoteMethod(garbage.address, "purge", new Object[]{garbage.id},
                                new Class[]{String.class}, new RequestOptions(ResponseMode.GET_FIRST, callTimeout));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        },0,10, TimeUnit.SECONDS);
    }

    public <T>T createRemoteObject(Class<T> proxyInterface, Address targetNode, Class c, Object[] args, Class[] argTypes) {
        try {
            Object[] remoteArgs = new Object[4];
            remoteArgs[0] = UUID.randomUUID().toString();
            remoteArgs[1] = c;
            remoteArgs[2] = args;
            remoteArgs[3] = argTypes;

            rpcDispatcher.callRemoteMethod(targetNode, "addObject", remoteArgs,
                    new Class[]{String.class, Class.class, Object[].class, Class[].class},
                    new RequestOptions(ResponseMode.GET_FIRST, callTimeout));

            // now get in interface stub for this object
            return getRemoteObject(targetNode, remoteArgs[0].toString(), proxyInterface);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    // Operation triggerd by RPC
    public void addObject(String id, Class c, Object[] args, Class[] parameterTypes) throws Exception {
        remoteObjects.put(id, c.getConstructor(parameterTypes).newInstance(args));
    }

    // Operation triggerd by RPC
    public Object invoke(String id, String methodName, Object[] args, Class[] argTypes) throws Exception {
        Object ro = remoteObjects.get(id);
        return ro.getClass().getMethod(methodName, argTypes).invoke(ro, args);
    }

    // Operation triggerd by RPC
    public void purge(String id) {
        System.out.println("garbage collecting: " + id);

        //return remoteObjects.remove(id) != null;
        remoteObjects.remove(id);
    }

    public <T>T getRemoteObject(final Address nodeAdress, final String id, final Class<T> clazz) {
        if (!clazz.isInterface()) throw new RuntimeException("Class has to be an interface!");

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
                Object[] remoteArgs = new Object[4];
                remoteArgs[0] = id;
                remoteArgs[1] = method.getName();
                remoteArgs[2] = args;
                remoteArgs[3] = method.getParameterTypes();

                // remote call
                return rpcDispatcher.callRemoteMethod(nodeAdress, "invoke",
                        remoteArgs, new Class[]{String.class, String.class, Object[].class, Class[].class},
                        new RequestOptions(ResponseMode.GET_FIRST, callTimeout));
            }
        };

        T result = (T) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class[]{clazz},
                handler);

        // use weak pointers to the proxy object here and if one is garbage collected, purge the remote object as well
        WeakReference<T> weakReference = new WeakReference<>(result, rq);
        grabageTracker.put(weakReference, new IdPointer(nodeAdress, id));

        return result;
    }

    public static void main(String[] args) throws Exception {
        Channel channel = new JChannel();
        channel.connect("test-cluster");

        List<Address> members = channel.getView().getMembers();
        RMILikeWrapper w = new RMILikeWrapper(channel);

        if (members.size() > 1) {
            System.out.println("send to " + members.get(0));
            FooInterface remoteObject = w.createRemoteObject(FooInterface.class, members.get(0), FooImpl.class, null, null);
            System.out.println(remoteObject.doSomething("Harr harr harr"));
            remoteObject = null;
        }

        System.out.println(channel.getView().getMembers());
    }
}

1 个答案:

答案 0 :(得分:1)

使用以下方法可以确定GC在弱引用上的行为方式。 选项1:

-verbose:gc

每当GC开始拍照时,此参数记录GC行为。如果要检查GC是否已执行,您可以获取日志文件,可以从GC日志中进行检查。对于交互式GC分析,请尝试使用http://www.ibm.com/developerworks/java/jdk/tools/gcmv/

的日志

选项2:

收集堆转储和用户事件,并将其加载到https://www.ibm.com/developerworks/java/jdk/tools/memoryanalyzer/

在OQL部分编写OQL(对象查询语言) select * from package(s).classname 然后点击!在工具栏上 它将给出该类型的对象列表 右键单击对象 - &gt; GC根的路径 - &gt;排除soft / weak / Phantom引用  如果可疑对象没有任何强引用,则它将显示NULL else 您将获得有关谁对可疑对象持强引用的信息。