Ale*_*nov 11 java puzzle garbage-collection java.util.concurrent
所有!
我在LinkedBlockingQueue中发现了奇怪的代码:
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
Run Code Online (Sandbox Code Playgroud)
谁能解释为什么我们需要局部变量h?它对GC有什么帮助?
如果你看一下jsr166 src那么你会发现有问题的提交
这表明答案在这个错误报告中
http://bugs.sun.com/view_bug.do?bug_id=6805775
完整的讨论在这个主题中
http://thread.gmane.org/gmane.comp.java.jsr.166-concurrency/5758
"帮助GC"一点是为了避免事情流失到终身.
干杯
马特
也许有点晚了,但目前的解释对我来说完全不令人满意,我想我有一个更合理的解释。
首先,每个 java GC 都以某种方式从根集进行某种跟踪。这意味着如果旧的 head 被收集,我们next无论如何都不会读取变量 - 没有理由这样做。因此,在下一次迭代中收集IF头并不重要。
上句中的 IF 是这里的重要部分。在不同的东西旁边设置之间的区别对于收集头本身并不重要,但可能会对其他对象产生影响。
让我们假设一个简单的分代 GC:如果 head 在年轻集合中,无论如何它将在下一个 GC 中被收集。但是如果它在旧集合中,它只会在我们进行很少发生的完整 GC 时被收集。
那么如果 head 在旧集合中并且我们执行年轻 GC 会发生什么?在这种情况下,JVM 假定旧堆中的每个对象都还活着,并将从旧对象到新对象的每个引用添加到新 GC 的根集。这正是赋值在这里避免的:写入旧堆通常受到写屏障或其他东西的保护,以便 JVM 可以捕获此类赋值并正确处理它们 - 在我们的例子中,它next从根集中删除指向的对象确实有后果。
简短示例:
假设我们有1 (old) -> 2 (young) -> 3 (xx)。如果我们现在从列表中删除 1 和 2,我们可能期望这两个元素都将被下一次 GC 收集。但是如果只发生了一个年轻的 GC 并且我们没有删除next旧的指针,那么元素 1 和 2 都不会被收集。与此相反,如果我们删除了 1 中的指针,则 2 将被年轻 GC 收集。
为了更好地理解发生的情况,让我们看看执行代码后列表的样子。首先考虑一个初始列表:
1 -> 2 -> 3
Run Code Online (Sandbox Code Playgroud)
然后h指向head和firstto h.next:
1 -> 2 -> 3
| |
h first
Run Code Online (Sandbox Code Playgroud)
然后h.next指向h并head指向first:
1 -> 2 -> 3
| / \
h head first
Run Code Online (Sandbox Code Playgroud)
现在,实际上我们知道只有一个活动引用指向第一个元素,它本身就是 ( h.next = h),而且我们还知道 GC 会收集没有更多活动引用的对象,因此当方法结束时,(旧的)列表的头部可以被 GC 安全地收集,因为h它只存在于该方法的范围内。
话虽如此,有人指出,并且我同意这一点,即使使用经典的出队方法(即仅指向first并head.next指向head)first,也不再有指向旧头的引用。然而,在这种情况下,旧的头在内存中悬空,并且其next字段仍然指向first,而在您发布的代码中,唯一剩下的就是一个指向自身的孤立对象。这可能会触发 GC 更快地采取行动。
| 归档时间: |
|
| 查看次数: |
949 次 |
| 最近记录: |