Oleg Ryaboy
Oleg Ryaboy

Reputation: 3069

Can Locks be garbage collected while locked?

Can Locks (java.util.concurrent.locks.Lock) be garbage collected while locked? Suppose a purely theoretical example:

WeakReference r;

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

Could lock be garbage collected after foo() gets executed? In other words, does lock.lock() create any strong references back to the lock? How do you know?

Upvotes: 13

Views: 2696

Answers (6)

Stephen C
Stephen C

Reputation: 719376

A locked Lock can be garbage collected when it is no longer reachable. (The definition of "reachable" in the JLS is: "A reachable object is any object that can be accessed in any potential continuing computation from any live thread." - JLS 12.16.1)

However, a locked Lock that some thread is waiting on must be executing one of the Lock's lock / tryLock instance methods. For this to happen, the thread must have a reference to the lock; i.e. one that the lock method is currently accessing. Therefore, a locked Lock that some thread is trying to acquire is reachable, and cannot be garbage collected.

In other words, does lock.lock() create any strong references back to the lock?

No. In your example, the strong reference exists in the form of the lock variable. But suppose that we tweaked your example to get rid of lock; e.g.

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

When you call get(), it will return a reference to the ReentrantLock object which will be held in a temporary variable or register, making it strongly reachable. It will continue to be strongly reachable as long as the lock() call is running. When the lock() call returns, the ReentrantLock object may become weakly reachable (again).

How do you know?

How do I know? A combination of:

  • knowledge of the Java Language Specification's definition of reachability and other things,
  • experience with implementing JVMs,
  • good old-fashioned logic, and ...
  • I confirmed it by reading the OpenJDK source code (though this doesn't prove anything about JVMs in general.)

There is not need to implement Lock using global queues, and hence no reason to have a hidden reference to the Lock object that would prevent it becoming unreachable. Furthermore, a Lock that could not be garbage collected when it was locked would be a storage leak, and a major implementation flaw, and I cannot imagine Doug Lea et al making that mistake!

Upvotes: 19

bestsss
bestsss

Reputation: 12066

Technically, the only objects that cannot be garbage collected are the classes loaded by the bootstrap classloader (the rest are outgoing references to the former)

(java.util.concurrent.locks.)Lock(s) are absolutely normal objects, not different than java.util.ArrayList in terms of garbage collection. I have written locks w/ LIFO semantics (or specifically AbstractQueueSynchronized that is not FIFO), that's useful to minimized cache misses since the hottest threads get to work even more. Point is that is absolutely possible to write your own custom locking/sync/atomic code.

Upvotes: 2

Oleg Ryaboy
Oleg Ryaboy

Reputation: 3069

It turns out that while we often conceptually think that threads 'obtain' and 'own' locks, it's actually not the case from the implementation perspective. The locks keep references to owning and waiting threads, while the threads have no references to locks, and have no knowledge of the locks they 'own'.

The ReentrantLock implementation is also rather straightforward: there are no static collections of locks, and there are no background maintenance threads that keep track of locks.

Neither creating, nor locking a lock creates any 'hidden' new strong references anywhere, so, in the example above, the lock can be garbage collected once foo() is done.

One can verify this by perusing the source code:

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

Upvotes: 2

Michael Burr
Michael Burr

Reputation: 340406

Assuming that you mean "after foo() gets executed", the answer is yes - that's really the point of WeakReferences.

You would know that because when you tried to convert the WeakReference r back into a regular (or strong) reference, you'd get null returned:

if (r.get() == null) {
    // the lock was garbage collected
}

Upvotes: 0

bmargulies
bmargulies

Reputation: 100151

It can be garbage collected while locked. No strong reference is created by taking the lock. As written, of course, you'd have to set lock to null and run the gc to see the reference go null.

Upvotes: 0

Reboot
Reboot

Reputation: 1744

The Lock is not unlike any other object. It depends if some internal Java mechanism references the Lock. But I see no reason why Java should keep a reference to the Lock.

Upvotes: 0

Related Questions