为了账号安全,请及时绑定邮箱和手机立即绑定

Java 是否保证当前同步的对象不会被垃圾回收?

Java 是否保证当前同步的对象不会被垃圾回收?

喵喔喔 2023-07-19 16:50:55
当线程持有其监视器时,是否可以保证对象不会被垃圾收集?例如class x {    private WeakReference<Object> r;    Object getMonitorObject() {        Object o = new Object();        r = new WeakReference<>(o);        return o;    }    void thread1() throws Exception {        synchronized (getMonitorObject()) {            Thread.sleep(3000);        }    }    void thread2() {        Object b = r.get();    }}具体来说,在这种情况下,是否有任何保证在另一个线程正在休眠时b将被调用非nullif ?我们假设整个过程是在另一个线程休眠时执行的。thread2()thread1()thread2()thread1()
查看完整描述

5 回答

?
红颜莎娜

TA贡献1842条经验 获得超12个赞

同步可以防止垃圾收集,但一般情况下不会。对于您的具体情况,我们无法保证。

与JLS §12.6.1比较

这种类型的转换可能会导致该finalize方法的调用比预期的更早发生。为了允许用户防止这种情况,我们强制执行同步可以使对象保持活动状态的概念。如果一个对象的终结器可以导致该对象上的同步,那么只要对该对象持有锁,该对象就必须处于活动状态并且被认为是可访问的。

请注意,这不会阻止同步消除:同步仅在终结器可能对其进行同步时才使对象保持活动状态。由于终结器发生在另一个线程中,因此在许多情况下无论如何都无法删除同步。

因此,由于您的对象没有自定义终结器,因此在终结过程中可能不会发生同步,原则上,您的对象是一个允许消除锁的临时对象,在这种情况下,它不会阻止垃圾收集。

但是存在一个实际障碍,即您存储 a 的WeakReference方式使得另一个线程可以在未收集该对象时检索该对象,一旦存在这种可能性,该对象就不再是本地的,并且无法应用锁消除。

在构造后立即积极收集对象(或完全消除其存在)并在其逃逸或WeakReference首先创建空对象之前清除弱引用的理论实现将符合规范,就像在该执行场景中一样,锁消除是有道理的。


请注意,即使您插入 a ,线程调用和另一个调用reachabilityFence之间也没有发生之前关系,因此第二个线程可能始终表现得好像在另一个线程完成块或通过可达性栅栏之后执行,即使您的真实线程生命时钟却另有说明。明确表示没有同步语义。thread1()thread2()thread2()synchronizedThread.sleep


查看完整回答
反对 回复 2023-07-19
?
12345678_0001

TA贡献1802条经验 获得超5个赞

为了让synchronized块的末尾删除监视器锁,该synchronized块必须保留对返回的对象的引用getMonitorObject()

该引用会阻止 GC,所以答案是肯定的。


查看完整回答
反对 回复 2023-07-19
?
慕森卡

TA贡献1806条经验 获得超8个赞

在我详细研究过的所有 Java 实现中,对象的原始互斥体或锁的状态在第1部分中由对象头中的位表示。当锁被释放时,JVM 需要更新标头位2,因此它仍然必须拥有对该对象的引用,无论是在堆栈上还是在寄存器中。

1 - 当发生互斥锁争用时,锁会“膨胀”以记录额外信息。所以我们不能说整个状态都在对象头中。

2 - 在大多数情况下,解锁代码不知道锁定对象是否不可访问。但如果它是由于积极的 JIT 编译器优化而实现的,那么假设它也可以知道不再需要更新对象头。


但是假设解锁互斥体时未使用/不需要对象引用。

以下是JLS 12.6.1中可达性的定义:

可到达的对象是可以在任何潜在的持续计算中从任何活动线程访问的任何对象。” “可以通过某些引用链从某些可终结的对象到达终结器可到达的对象,但不能从任何活动线程到达。”

“任何一种方法都无法到达无法到达的对象。”

为了使对象成为垃圾收集的候选对象,它必须不可访问。换句话说,它不能从任何活动线程访问。

锁定的互斥锁怎么样?好吧,线程的“潜在的持续计算”可能需要解锁互斥锁:

  • 如果互斥锁在没有对象引用的情况下无法解锁,则该对象是可达的。

  • 如果可以在不引用对象的情况下解锁互斥锁,那么该锁可能无法访问,或者终结器可访问。


可是等等 ...

在JLS 12.6.2中,有一些关于可达性决策点的复杂语言。

“在每个可达性决策点,某些对象集被标记为不可达,而这些对象的某些子集被标记为可终结。”

" 如果对象 X 在 di 处被标记为不可访问,则: - ... - 在 di 之后的线程 t 中对 X 的所有主动使用都必须发生在 X 的终结器调用中,或者作为线程 t 执行读取的结果出现在对 X 的引用的 di 之后;并且 - ...”

“当且仅当以下至少一项为真时,操作a才是主动使用: - ... - 锁定或解锁 ,并且 在调用终结器之后发生锁定操作. - .. ”。XaXXX

现在,如果我理解正确的话,那就是说,如果活动线程仍然可以释放相应的互斥锁,那么可终结的对象就无法被终结。


总之:

  1. 在当前的 JVM 中,互斥锁的锁定状态取决于对象头。如果互斥体仍然可以被线程释放,那么该对象必须是可访问的......作为实现细节。

  2. 假设存在一个 JVM,其中可以在不引用对象的情况下释放互斥锁,那么当对象仍处于锁定状态时,该对象可能无法访问。

  3. 但是,如果该对象是可终结的,那么直到所有可能需要释放锁的活动(应用程序)线程都完成了该操作之后,该对象才会被终结。


查看完整回答
反对 回复 2023-07-19
?
一只名叫tom的猫

TA贡献1906条经验 获得超2个赞

我刚刚在javadoc中看到了这样的“旁白”:

reachabilityFence“在本身确保可访问性的构造中不需要[方法]。例如,因为通常无法回收锁定的对象,所以如果在类 Resource 的所有方法(包括Finalize)被包含在同步(this)块中。”

这似乎是说,被锁定的对象不能被垃圾收集。

我不确定这是否优先于 JLS 12.6.1 和 12.6.2;请参阅我的其他答案,或者我们是否应该将其理解为仅适用于 Oracle / OpenJDK Java 类库的 Java(语言)实现。


查看完整回答
反对 回复 2023-07-19
?
RISEBY

TA贡献1856条经验 获得超5个赞

sleep 方法不会离开同步块,因此不会释放锁或监视器对象,它只是阻塞执行一段时间。由于锁从未被释放,垃圾收集器不会收集它,除非持有它的线程完成同步块的执行或使用wait()方法释放它。

所以,是的,如果有足够的时间完成thread2()调用,它保证不为空。thread1()getMonitorObject()

查看完整回答
反对 回复 2023-07-19
  • 5 回答
  • 0 关注
  • 120 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信