2010-07-29 2 views
6

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

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

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

ответ

3

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

+0

Так может ли это произойти и на виртуальной машине на основе стека? Теперь, когда я думаю об уровне байт-кода, как вы, я понимаю это намного лучше. Я думаю, что в случае стековой машины ссылка не должна быть в стеке для 'invokevirtual' для завершения? Итак, как только значение «myIndex» будет восстановлено, «это» может быть удалено из стека и потенциально исправлено? –

+0

В строгой реализации на основе стека это невозможно, так как этот указатель останется в стеке. Но с помощью метода inline, вне исполнения заказа и распределения регистров становится возможным одновременный вызов финализатора. См. Мою последнюю редакцию в этой продолжающейся саге. :) – mdma

+0

Отличный ответ! Я даже не знал никого, кроме Android Dalvik VM, внедрил основанную на регистре JVM. Это распространено? –

 Смежные вопросы

  • Нет связанных вопросов^_^