我正在阅读 AbstractQueuedSynchronizer 的源代码?我发现方法 cancelAcquire() 有点难以理解,最后一行让我感到困惑:
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary, although with
// a possibility that a cancelled node may transiently remain
// reachable.
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
pred.compareAndSetNext(predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
pred.compareAndSetNext(predNext, next);
} else {
unparkSuccessor(node);
}
node.next = node; // help GC !!!!CONFUSE ME!!!!
}
}
如你所见,最后一行让我感到困惑:
node.next = node; // help GC
为什么不直接将它设置为空:node.next=null,它也有助于 gc。任何人都可以帮忙吗?谢谢!
“node.next = node;”这一行用于通过打破链表中的潜在循环来帮助垃圾收集器 (GC)。如果“node.next”设置为“null”,它将在前一个节点 pred 的 next 字段中留下对节点的引用,这将防止节点被垃圾收集,即使它已从列表中删除。通过将“node.next”设置为 node,它创建了一个自循环,作为一个明确的标记,表明该节点已从列表中删除并且任何其他节点都无法再访问。当 GC 遇到自循环时,它知道可以安全地对对象进行垃圾回收。
如果系统中有其他对节点的引用,将 node.next 设置为 null 将不足以打破循环。相比之下,将 node.next 设置为 node 会向 GC 发出更强的信号,表明 node 不再被使用,因为它创建了一个唯一的引用返回到 node ,不能被任何其他对象共享。
这是因为跨代引用,即当老年代的对象引用了新生代的对象时,新生代的对象无法被回收
请想一想,当一个节点进入老年代,后来它出列了,但你还没有将它的 next 指针设置为它自己或 null。然后,当下一个节点出列时,即使它无法访问,它仍然不能被次要 GC 回收,因为它是由旧代中的节点链接的。在执行完整的 GC 之前,事情会变得越来越糟,因为它会同时寻找年轻一代和老一代,并收集所有无法访问的对象。
你可以查看这个视频从第23分钟开始,它说明了我上面提到的内容。
现在我们知道有必要在下一个节点出列之前切断与下一个节点的连接。但为什么要将
node.next
设置为自身而不是 null
。是因为null
的node.next
值有特殊含义,所以这个节点在队尾。
正如对房产的评论
所说:next
取消节点的下一个字段设置为指向节点本身而不是 null,以使 isOnSyncQueue 的生活更轻松。
基本上,这都是因为垃圾收集器的工作方式。 当连接到 obj1 的老一代中的 obj2 可达时,如果次要 GC 不收集年轻代中的 obj1 是可以的。因为我们还在使用 obj2。但是当 obj2 不可达时,如果 minor GC 仍然保留 obj1,就会造成麻烦,这就需要我们了解 GC 的那些细节,以防止这种情况发生。