Срок службы 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 позволяют мне сделать вывод, что:
- "Зеркальные" значения примитивов никогда не умирают, поскольку все примитивные значения живут в клиенте JDI до тех пор, пока его не нужно будет передать на сервер с использованием протокола JDWP. (Не было бы преимуществом сохранять дескриптор примитивного значения, которое существует только на сервере, в любом случае, так как для отправки дескриптора по транспорту потребуется столько же или больше байтов, как если бы он просто отправлял примитивное значение!)
- Любое зеркало ссылочного типа, включая ссылку на
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 никогда не собирать этот объект.