Java 锁可以在锁定时进行垃圾收集吗?
锁(java.util.concurrent.Locks.Lock)是否可以在锁定时进行垃圾收集? 假设一个纯理论的例子:Java 锁可以在锁定时进行垃圾收集吗?,java,multithreading,concurrency,locking,thread-safety,Java,Multithreading,Concurrency,Locking,Thread Safety,锁(java.util.concurrent.Locks.Lock)是否可以在锁定时进行垃圾收集? 假设一个纯理论的例子: WeakReference r; public void foo(){ Lock lock = new ReentrantLock(); r = new WeakReference(lock); lock.lock(); } 执行foo()后,lock是否会被垃圾收集?换句话说,lock.lock()是否创建任何指向锁的强
WeakReference r;
public void foo(){
Lock lock = new ReentrantLock();
r = new WeakReference(lock);
lock.lock();
}
执行foo()
后,lock
是否会被垃圾收集?换句话说,lock.lock()
是否创建任何指向锁的强引用?
你怎么知道的?这把锁和其他任何物体都一样。这取决于内部Java机制是否引用了锁。但是我看不出Java为什么要保留对锁的引用。它可以在锁定时被垃圾收集。获取锁不会创建强引用。如前所述,当然,您必须将lock设置为null并运行gc以查看引用变为null。假设您的意思是“在执行
foo()
之后”,答案是肯定的-这确实是weakreference的重点
您会知道这一点,因为当您尝试将WeakReference
r
转换回常规(或强)引用时,会返回null
:
if (r.get() == null) {
// the lock was garbage collected
}
当无法访问已锁定的
锁时,可以对其进行垃圾收集。(JLS中“可访问”的定义是:“可访问对象是指在任何活动线程的任何潜在连续计算中可以访问的任何对象。”—JLS 12.16.1)
但是,某个线程正在等待的已锁定的Lock
必须执行锁的Lock/tryLock实例方法之一。为了实现这一点,线程必须有一个对锁的引用;i、 e.锁定方法当前正在访问的对象。因此,某个线程试图获取的锁定锁是可访问的,不能被垃圾回收
换句话说,lock.lock()是否会创建回锁的强引用
否。在您的示例中,强引用以lock
变量的形式存在。但是假设我们对您的示例进行了调整,以消除锁
;e、 g
public void do_lock(WeakReference<ReentrantLock> r)
r.get().lock();
}
public void do_lock(WeakReference r)
r、 get().lock();
}
调用get()
时,它将返回对ReentrantLock
对象的引用,该对象将保存在临时变量或寄存器中,使其具有强可访问性。只要lock()
调用正在运行,它将继续具有很强的可访问性。当lock()
调用返回时,ReentrantLock
对象可能变得弱可及(再次)
你怎么知道的
我怎么知道?以下各项的组合:
- 了解Java语言规范对可达性和其他方面的定义
- 具有实施JVM的经验
- 好的老式逻辑,还有
- 我通过阅读OpenJDK源代码确认了这一点(尽管这并不能证明JVM的任何特性)
不需要使用全局队列实现Lock
,因此没有理由隐藏对Lock
对象的引用,以防止其无法访问。此外,锁
在被锁定时无法被垃圾收集,这将是一个存储泄漏,也是一个主要的实现缺陷,我无法想象Doug Lea等人会犯这样的错误 事实证明,虽然我们通常在概念上认为线程“获取”和“拥有”锁,但从实现的角度来看,事实并非如此。锁保留对拥有和等待线程的引用,而线程没有对锁的引用,也不知道它们“拥有”的锁
ReentrantLock实现也相当简单:没有静态锁集合,也没有跟踪锁的后台维护线程
创建或锁定锁都不会在任何地方创建任何“隐藏”的新强引用,因此,在上面的示例中,lock
可以在foo()
完成后进行垃圾收集
可以通过仔细阅读源代码来验证这一点:
从技术上讲,唯一不能被垃圾收集的对象是引导类加载器加载的类(其余是对前者的传出引用)
(java.util.concurrent.locks.)Lock是绝对正常的对象,在垃圾收集方面与java.util.ArrayList没有区别。我已经编写了带有后进先出语义的锁(或者特别是非FIFO的锁),这对于最小化缓存未命中非常有用,因为最热的线程可以工作得更多。关键是,您完全可以编写自己的自定义锁定/同步/原子代码。Stephen,您没有回答问题的“您怎么知道”部分。另外,从垃圾收集的角度来看,“某个线程正在等待”的锁与强引用引用的锁没有什么不同。当然,这个问题是关于没有强引用的锁。@Oleg Ryaboy-这个问题不是“当然”关于没有强引用的锁!您可能有意这么做,但问题中没有这样说。感谢您检查OpenJDK源代码。回答接受!关于锁的弱引用的实际用例,我有一个线程安全的弱引用回忆录,用于管理条带锁工厂。这样可以减少锁的最大数量(例如,通过一些算法,例如对数字键进行模块化),并有利于重新创建一段时间内不使用的锁,尽管最后一个属性在所讨论的锁是瘦/胖锁实现时最有用,但它确实降低了驻留内存的消耗。@Jed-Wesley-Smith-很有趣。你有没有做过调查,看看你的性能提高了多少/节省了多少内存?Michael,问题的“你怎么知道”部分是指“你怎么知道你的答案是正确的”。我了解到,当涉及到垃圾收集时,依靠意见和直觉并不总是最好的主意