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;
Это может быть связано с чем-то вроде этого псевдо 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;
Выполненный подобным образом, res
очищается после выполнения встроенного 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()
В отмеченном месте (<---) нет ссылок на экземпляр CleanResource, поэтому он подходит для сбора и вызова метода финализатора. Поскольку финализатор можно вызывать в любое время после очистки последней ссылки, возможно, что финализатор и оставшаяся часть <code>CleanResource.doSomething() будут выполняться параллельно.
EDIT2: keepAlive () обеспечивает доступ к указателю this
в конце метода, поэтому компилятор не может оптимизировать использование указателя. И что этот доступ гарантированно произойдет в указанном порядке (синхронизированное слово помечает забор, запрещающий переупорядочение операций чтения и записи до / после этой точки.)
Исходное сообщение:
В примере говорится, что вызывается метод doSomething, и после вызова данные, на которые ссылается указатель this
, могут быть прочитаны раньше (в этом примере myIndex
). После считывания ссылочных данных указатель this
в этом методе больше не требуется, и процессор / компилятор может перезаписать регистры / объявить объект как недоступный. Таким образом, сборщик мусора может одновременно вызывать финализатор в то время, когда выполняется метод doSomething () объекта.
Но так как указатель this
не используется, трудно понять, как это будет иметь ощутимый эффект.
РЕДАКТИРОВАТЬ: Хорошо, возможно, если есть кешированные указатели на поля объекта, доступ к которым осуществляется через кеш, вычисляется по this
до его восстановления, а затем объект восстанавливается, ссылки на память становятся недействительными. Есть часть меня, которой трудно поверить, что это возможно, но опять же, это действительно сложный поворотный случай, и я не думаю, что в JSR-133 есть что-то, что могло бы предотвратить это по умолчанию. Вопрос в том, считается ли на объект ссылками только указатели на его базу или же указатели на его поля.