我正在使用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());
}
}
答案 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 您将获得有关谁对可疑对象持强引用的信息。