Могут ли замки собираться мусором, пока они заперты? - PullRequest
14 голосов
/ 28 декабря 2010

Могут ли замки (java.util.concurrent.locks.Lock) собираться мусором во время блокировки?Предположим, чисто теоретический пример:

WeakReference r;

public void foo(){
       Lock lock = new ReentrantLock();
       r = new WeakReference(lock);   
       lock.lock();
}

Может ли lock быть собранным мусором после выполнения foo()?Другими словами, lock.lock() создает какие-либо сильные ссылки обратно на блокировку?Откуда ты знаешь?

Ответы [ 6 ]

20 голосов
/ 28 декабря 2010

Заблокированный Lock может быть собран мусором, когда он больше недоступен.(Определение «достижимости» в JLS: «Достижимый объект - это любой объект, к которому можно получить доступ в любом потенциальном продолжающемся вычислении из любого живого потока.» - JLS 12.16.1)

Однако заблокированный Lock, который ожидает некоторый поток на , должен выполнять один из методов экземпляра Lock / tryLock.Для этого поток должен иметь ссылку на блокировку;то есть тот, к которому в настоящее время обращается метод блокировки.Следовательно, заблокированная блокировка, которую пытается получить какой-то поток, достижима и не может быть собрана сборщиком мусора.

Другими словами, создает ли lock.lock () какие-либо сильные ссылки обратно на блокировку?

Нет.В вашем примере сильная ссылка существует в виде переменной lock.Но предположим, что мы настроили ваш пример, чтобы избавиться от lock;Например,

public void do_lock(WeakReference<ReentrantLock> r) 
   r.get().lock();
}

Когда вы вызываете get(), он вернет ссылку на объект ReentrantLock, который будет храниться во временной переменной или регистре, что делает его полностью достижимым.Он будет оставаться доступным до тех пор, пока выполняется вызов lock().Когда возвращается вызов lock(), объект ReentrantLock может стать слабо доступным (снова).

Откуда вы знаете?

Откуда мне знать?Сочетание:

  • знание определения достижимости Спецификации языка Java и других вещей,
  • опыта реализации JVM,
  • старой доброй старомодной логики и...
  • Я подтвердил это, прочитав исходный код OpenJDK (хотя это не доказывает ничего о JVM в целом.)

Там нетнеобходимо реализовать Lock, используя глобальные очереди, и, следовательно, нет причин иметь скрытую ссылку на объект Lock, который бы препятствовал тому, чтобы он стал недоступным.Кроме того, Lock, который нельзя было собрать мусором, когда он был заблокирован, будет утечкой памяти и серьезным недостатком реализации, и я не могу представить Даг Ли и др., Совершающих эту ошибку!

2 голосов
/ 04 января 2011

Технически, единственными объектами, которые не могут быть собраны сборщиком мусора, являются классы, загружаемые загрузчиком классов начальной загрузки (остальные являются исходящими ссылками на первый)

(java.util.concurrent.locks.) Блокировки - это абсолютно нормальные объекты, не отличающиеся от java.util.ArrayList с точки зрения сборки мусора. Я написал блокировки с семантикой LIFO (или, в частности, с AbstractQueueSynchronized, который не является FIFO), это полезно для минимизации пропусков кэша, поскольку самые горячие потоки начинают работать еще больше. Дело в том, что абсолютно возможно написать свой собственный код блокировки / синхронизации / атомарного кода.

2 голосов
/ 28 декабря 2010

Оказывается, что, хотя мы часто концептуально думаем, что потоки «получают» и «собственные» блокировки, это на самом деле не так с точки зрения реализации.Блокировки сохраняют ссылки на владеющие и ожидающие потоки, в то время как потоки не имеют ссылок на блокировки и не знают о блокировках, которыми они «владеют».

Реализация ReentrantLock также довольно проста: нет статических наборов блокировок и нет потоков фонового обслуживания, которые отслеживают блокировки.

Ни создание, ни блокировка блокировки не создают никаких «скрытых» новых сильных ссылок в любом месте, поэтому, в приведенном выше примере, lock можно собирать мусором после выполнения foo().

В этом можно убедиться, просмотрев исходный код:

http://fuseyism.com/classpath/doc/java/util/concurrent/locks/ReentrantLock-source.html

http://fuseyism.com/classpath/doc/java/util/concurrent/locks/AbstractQueuedSynchronizer-source.html

http://fuseyism.com/classpath/doc/java/util/concurrent/locks/AbstractOwnableSynchronizer-source.html

1 голос
/ 28 декабря 2010

Предполагая, что вы имеете в виду «после того, как foo() будет выполнено», ответ будет «да» - в этом и заключается смысл WeakReferences.

Вы бы знали, что, поскольку при попытке преобразовать WeakReference r обратно в обычную (или сильную) ссылку, вы получите null return:

if (r.get() == null) {
    // the lock was garbage collected
}
0 голосов
/ 28 декабря 2010

Это может быть сбор мусора в заблокированном состоянии. При взятии блокировки сильная ссылка не создается. Как написано, конечно, вам нужно установить для блокировки значение null и запустить gc, чтобы увидеть, что ссылка становится пустой.

0 голосов
/ 28 декабря 2010

Замок не похож на любой другой объект.Это зависит, если какой-то внутренний механизм Java ссылается на Lock.Но я не вижу причин, по которым Java должна сохранять ссылку на Lock.

...