锁在锁定时可以进行垃圾收集吗?

发布于 2024-10-09 19:56:40 字数 331 浏览 0 评论 0原文

锁(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

何必那么矫情 2024-10-16 19:56:40

当锁定的 Lock 无法再访问时,可以对其进行垃圾回收。 (JLS 中“可达”的定义是:“可达对象是可以在任何潜在的持续计算中从任何活动线程访问的任何对象。” - JLS 12.16.1)

但是,某个线程正在等待的锁定Lock必须正在执行Lock的lock/tryLock实例方法之一。为此,线程必须拥有对锁的引用;即lock方法当前正在访问的一个。因此,某个线程尝试获取的锁定的 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 将导致存储泄漏,并且是一个主要的实现缺陷,我无法想象 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.

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!

絕版丫頭 2024-10-16 19:56:40

事实证明,虽然我们经常在概念上认为线程“获取”和“拥有”锁,但从实现的角度来看实际上并非如此。锁保留对拥有和等待线程的引用,而线程没有对锁的引用,并且不知道它们“拥有”的锁。

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 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

万劫不复 2024-10-16 19:56:40

从技术上讲,唯一不能被垃圾收集的对象是引导类加载器加载的类(其余的是对前者的传出引用)

(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.

不…忘初心 2024-10-16 19:56:40

锁与任何其他对象都没有什么不同。这取决于某些内部 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.

一念一轮回 2024-10-16 19:56:40

锁定时可以对其进行垃圾收集。获取锁不会创建强引用。当然,正如所写,您必须将 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.

三月梨花 2024-10-16 19:56:40

假设您的意思是“在 foo() 执行之后”,答案是肯定的 - 这确实是 WeakReferences 的要点。

您会知道,因为当您尝试将 WeakReference r 转换回常规(或强)引用时,您将返回 null

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

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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文