Java GC Вопрос: Как объект может стать недоступным, пока один из его методов еще выполняется? - PullRequest
6 голосов
/ 29 июля 2010

Я читал эти слайды о финализаторах Java.В нем автор описывает сценарий (на слайде 33), согласно которому потоком финализатора может выполняться CleanResource.finalize(), в то время как CleanResource.doSomething() все еще выполняется в другом потоке.Как это могло произойти?

Если doSomething() является нестатическим методом, то для выполнения этого метода кто-то где-то должен иметь сильную ссылку на него ... верно?Так как же эта ссылка может быть очищена до возврата метода?Может ли другой поток подключиться и обнулить эту ссылку?Если бы это случилось, doSomething() все еще нормально вернулся бы в исходном потоке?

Это все, что я действительно хочу знать, но для ответа действительно выше и выше вы можете сказатьМне почему doSomething() на слайде 38 лучше, чем doSomething() на слайде 29. Почему достаточно просто вызвать этот метод keepAlive()?Разве вам не нужно заключать весь вызов в myImpl.doSomething() в блок synchronized(this){}?

1 Ответ

3 голосов
/ 29 июля 2010

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 есть что-то, что могло бы предотвратить это по умолчанию. Вопрос в том, считается ли на объект ссылками только указатели на его базу или же указатели на его поля.

...