EDIT3:
Результатом является то, что финализации и регулярный метод может быть выполнен одновременно на одной и той же инстанции. Вот объяснение того, как это может произойти. Код по существу:
class CleanResource {
int myIndex;
static ArrayList<ResourceImpl> all;
void doSomething() {
ResourceImpl impl = all.get(myIndex);
impl.doSomething();
}
protected void finalize() { ... }
}
Учитывая этот код клиента:
CleanResource resource = new CleanResource(...);
resource.doSomething();
resource = null;
Это может быть JITed к чему-то вроде этого псевдо C
register CleanResource* res = ...; call ctor etc..
// inline CleanResource.doSomething()
register int myIndex = res->MyIndex;
ResourceImpl* impl = all->get(myInddex);
impl->DoSomething();
// end of inline CleanResource.doSomething()
res = null;
Executed так, res
очищается после inlined CleanResource.doSomething()
, поэтому gc не произойдет до тех пор, пока этот метод не завершит выполнение. Невозможно завершить выполнение одновременно с другим методом экземпляра в том же экземпляре.
Но, запись в res
не используются после этой точки, и при условии, что нет никаких заборов, он может быть перемещен в начале исполнения, сразу после записи:
register CleanResource* res = ...; call ctor etc..
// inline CleanResource->doSomething()
register int myIndex = res->MyIndex;
res = null; /// <-----
ResourceImpl* impl = all->get(myInddex);
impl.DoSomething();
// end of inline CleanResource.doSomething()
В отмеченном location (< ---), нет ссылок на экземпляр CleanResource, и поэтому он имеет право на сбор и вызов метода finalizer. Поскольку финализатор можно вызывать в любое время после очистки последней ссылки, возможно, чтобы финализатор и остальная часть CleanResource.doSomething()
выполнялись параллельно.
EDIT2: KeepAlive() обеспечивает доступ к указателю this
в конце метода, поэтому компилятор не может оптимизировать использование указателя. И что этот доступ гарантированно произойдет в указанном порядке (синхронизированная слово знаменует собой забор, который запрещает изменение порядка чтения и записи до/после этой точки.)
исходное сообщение:
пример говорит что метод doSomething вызывается и один раз вызывается, данные, на которые ссылается указатель this
, могут быть прочитаны в начале (myIndex
).После считывания ссылочных данных в этом методе больше не нужен указатель this
, и cpu/compiler может перезаписать регистры/объявить объект как более недоступный. Таким образом, GC затем может одновременно вызывать финализатор одновременно с запуском метода doSomething() объекта.
Но так как указатель this
не используется, трудно понять, как это будет иметь ощутимый эффект.
EDIT: Возможно, если к полям объектов, к которым обращаются через кеш, есть кешированные указатели, вычисленные из , прежде чем они будут восстановлены, и затем объект будет восстановлен, ссылки на память станут недействительными. Есть часть меня, которая с трудом верит, что это возможно, но опять же, это кажется сложным угловым случаем, и я не думаю, что в JSR-133 есть что-то, чтобы предотвратить это по умолчанию. Речь идет о том, считается ли объект ссылкой только указателями на его базу или указателями на его поля.
Так может ли это произойти и на виртуальной машине на основе стека? Теперь, когда я думаю об уровне байт-кода, как вы, я понимаю это намного лучше. Я думаю, что в случае стековой машины ссылка не должна быть в стеке для 'invokevirtual' для завершения? Итак, как только значение «myIndex» будет восстановлено, «это» может быть удалено из стека и потенциально исправлено? –
В строгой реализации на основе стека это невозможно, так как этот указатель останется в стеке. Но с помощью метода inline, вне исполнения заказа и распределения регистров становится возможным одновременный вызов финализатора. См. Мою последнюю редакцию в этой продолжающейся саге. :) – mdma
Отличный ответ! Я даже не знал никого, кроме Android Dalvik VM, внедрил основанную на регистре JVM. Это распространено? –