Java 在使用局部变量之前,将引用复制到局部变量是否重要

Java 在使用局部变量之前,将引用复制到局部变量是否重要,java,Java,在OpenJDK 8中研究java.util.LinkedList的代码时,我发现了以下代码。代码很简单,但我对在第二行代码中将对第一个节点的引用保存为常量感到困惑。据我所知,这段代码将内联到一个行程序中,而无需复制引用。我说得对吗?如果是这样,为什么在这种和类似的情况下需要复制引用(这种习惯用法可以在java.util.LinkedList中的一半方法中找到) publicepeek(){ 最终节点f=第一个节点; 返回(f==null)?null:f.项; } 我的第一个想法是,它在某种程

在OpenJDK 8中研究java.util.LinkedList的代码时,我发现了以下代码。代码很简单,但我对在第二行代码中将对第一个节点的引用保存为常量感到困惑。据我所知,这段代码将内联到一个行程序中,而无需复制引用。我说得对吗?如果是这样,为什么在这种和类似的情况下需要复制引用(这种习惯用法可以在java.util.LinkedList中的一半方法中找到)

publicepeek(){
最终节点f=第一个节点;
返回(f==null)?null:f.项;
}
我的第一个想法是,它在某种程度上有助于并发,但LinkedList不允许并发访问(除非您自己承担风险),所以我想这是对优化器的一些提示,但无法理解它应该如何工作

我的第一个想法是它在某种程度上有助于并发性

…所以我想这是对优化器的一些提示

两者都有。:-)
LinkedList
不支持并发的事实并不意味着作者无论如何都不会遵循良好的实践,它告诉编译器和JIT他们只应该首先查找

如果没有
f
局部变量,我们将有:

public E peek() {
    return (this.first == null) ? null : this.first.item;
}
我添加了隐含的
this.
以强调
first
是一个实例字段

因此,如果在线程A上对
this.first==null
部分求值,那么
this.first
在线程B上更改,当
this.first.item
在线程A上求值时,它可能会抛出,因为
this.first
同时变为
null
。对于
f
,这是不可能的,因为
f
是本地的;只有运行
peek
调用的线程才会看到它


final
部分在代码文档中很好(因为作者从未打算更改
f
的值),并且向优化器提示我们永远不会更改
f
,这意味着当需要进行优化时,它可以将其优化到其寿命的一英寸以内,知道它只需要读取
这个。首先
一次,然后可以使用寄存器或堆栈值进行
null检查和返回。

AFAIK,使用堆栈上的引用要比使用堆上的引用快,或者至少过去是这样。因此建议这样做,尤其是在方法中多次使用引用时。我怀疑这在今天会有很大的不同,因为JIT可能会优化一行的。可能的重复。这是一个微观优化。可能的情况是,如果在执行此方法的过程中,
first
可能会发生变化,他们希望确保所有测试都在同一个引用上完成。您不想先测试
是否为非null,然后让它从您的下方更改,然后在它可能已更改为null时尝试取消引用它。他们使用final来告诉编译器一旦指定,就不要更改它。基本上是说1)将对象放在第一个节点的前面。2) 若要避免在f为null时使用null指针,请返回null,否则返回节点f内的“项”。因此,实际上,这只是分配它们并在第一个节点内返回项目的问题。顺便说一句,如果节点为null,则返回null,这样就不会出现null指针,并且(tl;tr:这是一种性能优化的极端形式,并带来了一种(非常)有限的“线程安全性”)IIRC
final
局部变量仅存在于Java源代码中,而不存在于字节码中。因此优化器不会看到它(这没有问题,因为它可以轻松确定变量是否被重新分配)。@maaartinus:在字节码级别,局部变量存在;如果您查看
LinkedList#peek
的字节码,您将看到
getfield
首先是
,然后是
astore\u 1
,然后该方法的所有其余部分都对该局部变量1进行操作。JIT可能更进一步,但最初变量是存在的。所以,你是说OP建议JIT将f内联到这个。f是错误的。我发现我的公式太草率了。我的意思是:局部变量的
final
修饰符不存在。省略修饰符可能会导致完全相同的字节码(AFAIK javac可以自由进行优化,其结果可能不同)。@maaartinus这是正确的<局部变量的code>final
仅存在于
javac
中,不会插入任何障碍(与通过构造函数设置的
final
相反)
public E peek() {
    return (this.first == null) ? null : this.first.item;
}