0

我一直在编写一个 Java 客户端,它使用JDI在远程 JVM 中创建和修改对象(通过连接到在远程 JVM 中运行的基于JDWP代理的服务器)。我的项目的要求之一是我不能暂停远程 JVM 中的所有线程,这意味着我创建的对象在我可以在 JVM 中访问它们之前可能会受到垃圾收集的影响。

在某些情况下,我在远程 JVM 中创建对象,但它们是随机被垃圾收集的。例如,如果我通过 在远程 JVM 中创建一个数组ArrayType.newInstance(int),有时该数组将被垃圾收集,然后我才能使其从远程 JVM 中的另一个可访问对象“可访问”。

例如,如果我试图将新数组存储到现有可访问对象的字段中,则调用ObjectReference.setValue(Field, Value)可能会随机抛出一个. ObjectCollectedException):

void createAndStoreArray(ObjectReference reachableObj, Field fieldOfObj, ArrayType type, int length)
{
    ArrayReference ref = type.newInstance(length);
    reachableObj.setValue(fieldOfObj, ref); // Sometimes throws ObjectCollectedException because ref's mirror garbage gets collected before I can store it on the reachable object
}

从理论上讲,aObjectReference的镜像甚至可以在我能够调用之前被垃圾收集ObjectReference.disableCollection()(无论如何,由于其他原因,我不想采取这个步骤)。

所以我的问题是,JDI 上是否有任何记录在案Value的寿命保证?

  • 远程 JVM 中原始值的镜像是否免于 GC?(有人认为会是,但方法文档中没有任何说明。)VirtualMachine. mirror*()
  • 字符串的镜像是否免于 GC?(有人会认为不是,但 JavaDoc 似乎保持沉默。)
  • 我假设任何其他人ObjectReference都可以随时进行 GC,除非您之前设法在其上禁用 GC?

在此先感谢您的帮助!

4

2 回答 2

2

虽然我找不到任何文档从我想要的角度解决问题,但结合查看GrepCodecom.sun.tools.jdi上的包的源代码和Oracle 网站上的 JDWP 协议规范,我得出以下结论:

  1. “镜像的”原始值永远不会消失,因为整个原始值都存在于 JDI 客户端中,直到需要使用 JDWP 协议将其传输到服务器。(在任何情况下,保留一个只存在于服务器中的原始值的句柄不会有任何优势,因为通过传输发送句柄需要与仅发送原始值一样多或更多的字节!
  2. 任何引用类型的镜像,包括对String创建者的引用,VirtualMachine.mirrorOf(String)都可以随时进行垃圾收集。

作为对 #1 的支持,请参见例如源代码com.sun.tools.jdi.VirtualMachineImpl.mirrorOf(long):它只是实例化一个新实例LongValueImpl并返回:

public LongValue mirrorOf(long value) {
    validateVM();
    return new LongValueImpl(this,value);
}

并且您可以看到,它LongValueImpl的构造函数和它的任何超类的构造函数都MirrorImpl无法通过 JDWP 传输传输数据,从而改变服务器 JVM 的状态。

与#2 对比。JDI JavaDoc的出发点是任何ObjectReference的镜像对象都可以随时被垃圾回收:

如果镜像对象已被垃圾收集,则任何ObjectReference直接或间接 ObjectReference作为参数的方法都可能抛出。ObjectCollectedException

来源证实了这一点com.sun.tools.jdi.VirtualMachineImpl.mirrorOf(String),它与服务器中的 JDWP 代理对话。这只是确认任何以这种方式创建的ObjectReferenceaStringReference在任何时候都容易受到 GC 的影响,包括立即......

public StringReference mirrorOf(String value) {
    validateVM();
    try {
        return (StringReference)JDWP.VirtualMachine.CreateString.
            process(vm, value).stringObject;
    } catch (JDWPException exc) {
        throw exc.toJDIException();
    }
}

据我所知,如果您有任何ObjectReference尝试,您需要在一个循环中保护所有与远程 JVM 中的镜像对象交互的尝试,ObjectCollectedException除非远程 JVM 中的所有线程都被挂起,否则该循环会捕获。例如,如果您的 JDI 客户端中有一个String在远程 JVM 中创建 a 并返回对它的非垃圾回收引用的方法,您可能会按照以下方式执行一些操作:

StringReference safeStringRef(VirtualMachine vm, String string) {
    ObjectCollectedException lastCause = null;
    for (int numTries = 0; numTries < SANE_TRY_LIMIT; ++numTries) {
        StringReference stringRef = vm.mirrorOf(string);
        try {
            stringRef.disableCollection();
            return stringRef;
        } catch (ObjectCollectedException e) {
            lastCause = e;
        }
    }
    throw new RuntimeException("Can't create safe string reference", lastCause);
}
于 2014-09-11T20:47:06.883 回答
0

可能对您有用的一件事是ObjectReference.disableCollection(),它指示已调试的 JVM 永远不会收集该对象。

于 2015-01-31T11:09:08.350 回答