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

锁(java.util.concurrent.locks.Lock)可以在锁定时被垃圾回收吗?假设一个纯理论的例子:

WeakReference r;

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

执行后可能会被垃圾回收?换句话说,是否创建了任何对锁的强引用?你怎么知道?lockfoo()lock.lock()


答案 1

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

但是,某些线程正在等待的锁定必须正在执行锁定的锁定/tryLock 实例方法之一。为此,线程必须具有对锁的引用;即锁定方法当前正在访问的一个。因此,某些线程尝试获取的锁定锁是可访问的,并且无法进行垃圾回收。Lock

换句话说,lock.lock() 是否创建了任何对锁的强引用?

不。在您的示例中,强引用以变量的形式存在。但是假设我们调整了您的示例以摆脱 ;例如:locklock

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

当您调用 时,它将返回对对象的引用,该对象将保存在临时变量或寄存器中,使其强可访问。只要呼叫正在运行,它将继续保持强力可访问性。当调用返回时,对象可能会变得弱可访问(再次)。get()ReentrantLocklock()lock()ReentrantLock

你怎么知道?

我怎么知道?以下各项的组合:

  • 了解Java语言规范对可访问性和其他事物的定义,
  • 实施JVM的经验,
  • 良好的老式逻辑,和...
  • 我通过阅读OpenJDK源代码证实了这一点(尽管这并不能证明JVM的任何内容。

不需要使用全局队列来实现,因此没有理由对对象进行隐藏引用以防止其变得无法访问。此外,一个在被锁定时无法被垃圾收集的将是存储泄漏,并且是一个重大的实现缺陷,我无法想象Doug Lea等人会犯这个错误!LockLockLock


答案 2

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

ReentrantLock 实现也相当简单:没有锁的静态集合,也没有跟踪锁的后台维护线程。

无论是创建锁还是锁定锁,都不会在任何地方创建任何“隐藏”的新强引用,因此,在上面的示例中,一旦完成,就可以进行垃圾回收。lockfoo()

可以通过仔细阅读源代码来验证这一点:

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


推荐