我正在考虑在一个计算密集的程序上使用Scala。对我们代码的C ++版本进行概要分析表明,我们可以从Lazy评估中获益。我已经在Scala 2.9.1中尝试过并且非常喜欢它。但是,当我通过反编译器运行该类时,实现看起来并不正确。我假设它是反编译器的神器,但我想得到一个更确定的答案......
考虑以下简单的例子:
class TrivialAngle(radians : Double)
{
lazy val sin = math.sin(radians)
}
当我反编译它时,我明白了:
import scala.ScalaObject;
import scala.math.package.;
import scala.reflect.ScalaSignature;
@ScalaSignature(bytes="omitted")
public class TrivialAngle
implements ScalaObject
{
private final double radians;
private double sin;
public volatile int bitmap$0;
public double sin()
{
if ((this.bitmap$0 & 0x1) == 0);
synchronized (this)
{
if (
(this.bitmap$0 & 0x1) == 0)
{
this.sin = package..MODULE$.sin(this.radians);
this.bitmap$0 |= 1;
}
return this.sin;
}
}
public TrivialAngle(double radians)
{
}
}
对我来说,返回区块位于错误的位置,您将始终获得锁定。这不是真正的代码所做的,但我无法证实这一点。任何人都可以确认或否认我有一个伪造的反编译,并且懒惰的实现有点合理(即,只有在计算值时锁定,并且没有为后续调用获取锁定?)
谢谢!
作为参考,这是我使用的反编译器: http://java.decompiler.free.fr/?q=jdgui
答案 0 :(得分:9)
我使用javap -c
获得的内容与您的反编译不符。特别是,当发现字段被初始化时,没有监视器输入。版本2.9.1也是。当然,挥发性访问仍然存在内存障碍,因此它并非完全免费。以///
开头的评论是我的
public double sin();
Code:
0: aload_0
1: getfield #14; //Field bitmap$0:I
4: iconst_1
5: iand
6: iconst_0
7: if_icmpne 54 /// if getField & 1 == O goto 54, skip lock
10: aload_0
11: dup
12: astore_1
13: monitorenter
/// 14 to 52 reasonably equivalent to synchronized block
/// in your decompiled code, without the return
53: monitorexit
54: aload_0
55: getfield #27; //Field sin:D
58: dreturn /// return outside lock
59: aload_1 /// (this would be the finally implied by the lock)
60: monitorexit
61: athrow
Exception table:
from to target type
14 54 59 any
答案 1 :(得分:9)
scala -Xprint:jvm
揭示了真实的故事:
[[syntax trees at end of jvm]]// Scala source: lazy.scala
package <empty> {
class TrivialAngle extends java.lang.Object with ScalaObject {
@volatile protected var bitmap$0: Int = 0;
<paramaccessor> private[this] val radians: Double = _;
lazy private[this] var sin: Double = _;
<stable> <accessor> lazy def sin(): Double = {
if (TrivialAngle.this.bitmap$0.&(1).==(0))
{
TrivialAngle.this.synchronized({
if (TrivialAngle.this.bitmap$0.&(1).==(0))
{
TrivialAngle.this.sin = scala.math.`package`.sin(TrivialAngle.this.radians);
TrivialAngle.this.bitmap$0 = TrivialAngle.this.bitmap$0.|(1);
()
};
scala.runtime.BoxedUnit.UNIT
});
()
};
TrivialAngle.this.sin
};
def this(radians: Double): TrivialAngle = {
TrivialAngle.this.radians = radians;
TrivialAngle.super.this();
()
}
}
}
这是一个(自JVM 1.5起)安全且非常快速的双重检查锁。
更多详情:
What's the (hidden) cost of Scala's lazy val?
请注意,如果一个类中有多个lazy val成员,则只能同时初始化其中一个成员,因为它们由synchronized(this) { ... }
保护。