Срок службы JDI-зеркал объектов, живущих в удаленной JVM

Я писал Java-клиент, который использует JDI для создания и изменения объектов в удаленной JVM (путем подключения к серверу на основе агента JDWP, работающему в удаленной JVM). Одним из требований моего проекта является то, что я не могу приостановить все потоки в удаленной 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
}

В теории, ObjectReference зеркало можно даже собрать мусором, прежде чем я смогу позвонить ObjectReference.disableCollection() (это шаг, который я не хочу делать по другим причинам в любом случае).

Итак, мой вопрос, есть ли какие-либо документированные гарантии продолжительности жизни на JDI Value s?

  • Зеркало примитивного значения освобождается от GC в удаленной JVM? (Один предполагает, что это будет, но ни один из VirtualMachine. mirror*() методы документы говорит что-нибудь.)
  • Зеркало Струны освобождено от GC? (Можно подумать, что нет, но JavaDoc, кажется, молчит.)
  • Я предполагаю, что любой другой ObjectReference может быть GC'd в любое время, если вам не удастся отключить GC на нем раньше?

Заранее спасибо за помощь!

2 ответа

Решение

Хотя я не могу найти какую-либо документацию, касающуюся вещей именно под тем углом, который я хотел, комбинация взгляда на исходный код для com.sun.tools.jdi пакет на GrepCode и спецификация протокола JDWP на веб-сайте Oracle позволяют мне сделать вывод, что:

  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 на сервере. Это просто подтверждение того, что как и любой ObjectReference StringReference созданный таким образом восприимчив к 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 и возвращает ссылку на сборщик мусора, вы можете сделать что-то вроде этого:

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);
}

Одна вещь, которая может быть вам полезна, это ObjectReference.disableCollection (), которая сигнализирует отлаженной JVM никогда не собирать этот объект.

Другие вопросы по тегам