锁在锁定时可以进行垃圾收集吗?
锁(java.util.concurrent.locks.Lock)在锁定时可以被垃圾收集吗? 假设一个纯粹理论上的例子:
WeakReference r;
public void foo(){
Lock lock = new ReentrantLock();
r = new WeakReference(lock);
lock.lock();
}
lock
可以在 foo()
执行后被垃圾回收吗?换句话说,lock.lock() 是否会创建任何对锁的强引用? 你怎么知道?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
当锁定的
Lock
无法再访问时,可以对其进行垃圾回收。 (JLS 中“可达”的定义是:“可达对象是可以在任何潜在的持续计算中从任何活动线程访问的任何对象。” - JLS 12.16.1)但是,某个线程正在等待的锁定
Lock
必须正在执行Lock的lock/tryLock实例方法之一。为此,线程必须拥有对锁的引用;即lock方法当前正在访问的一个。因此,某个线程尝试获取的锁定的 Lock 是可达的,并且不能被垃圾收集。不。在您的示例中,强引用以
lock
变量的形式存在。但是假设我们调整了您的示例以摆脱锁定;例如,当您调用
get()
时,它将返回对ReentrantLock
对象的引用,该对象将保存在临时变量或寄存器中,使其强可达。只要lock()
调用正在运行,它将继续保持强可达性。当lock()
调用返回时,ReentrantLock
对象可能(再次)变得弱可达。我怎么知道?结合了:
不需要使用全局队列实现
Lock
,因此没有理由隐藏对Lock
对象的引用这将防止它变得无法访问。此外,锁定时无法进行垃圾收集的Lock
将导致存储泄漏,并且是一个主要的实现缺陷,我无法想象 Doug Lea 等人会这样做错误!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.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 oflock
; e.g.When you call
get()
, it will return a reference to theReentrantLock
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 thelock()
call is running. When thelock()
call returns, theReentrantLock
object may become weakly reachable (again).How do I know? A combination of:
There is not need to implement
Lock
using global queues, and hence no reason to have a hidden reference to theLock
object that would prevent it becoming unreachable. Furthermore, aLock
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!事实证明,虽然我们经常在概念上认为线程“获取”和“拥有”锁,但从实现的角度来看实际上并非如此。锁保留对拥有和等待线程的引用,而线程没有对锁的引用,并且不知道它们“拥有”的锁。
ReentrantLock 实现也相当简单:没有静态的锁集合,也没有跟踪锁的后台维护线程。
创建或锁定锁都不会在任何地方创建任何“隐藏”的新强引用,因此,在上面的示例中,一旦
foo()
完成,lock
就可以被垃圾回收。人们可以通过仔细阅读源代码来验证这一点:
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
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 oncefoo()
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
从技术上讲,唯一不能被垃圾收集的对象是引导类加载器加载的类(其余的是对前者的传出引用)
(java.util.concurrent.locks。)Lock(s)绝对是普通对象,与java.util.ArrayList 在垃圾回收方面。我已经编写了带有 LIFO 语义的锁(或者特别是 AbstractQueueSynchronized,它不是 FIFO),这对于最大限度地减少缓存未命中很有用,因为最热的线程可以更多地工作。要点是完全可以编写您自己的自定义锁定/同步/原子代码。
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.
锁与任何其他对象都没有什么不同。这取决于某些内部 Java 机制是否引用了 Lock。但我认为 Java 没有理由保留对锁的引用。
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.
锁定时可以对其进行垃圾收集。获取锁不会创建强引用。当然,正如所写,您必须将 lock 设置为 null 并运行 gc 才能看到引用变为 null。
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.
假设您的意思是“在 foo() 执行之后”,答案是肯定的 - 这确实是 WeakReferences 的要点。
您会知道,因为当您尝试将
WeakReference
r
转换回常规(或强)引用时,您将返回null
: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 getnull
returned: