scala的lazy关键字字节码分析
先简单说下lazy关键字的作用:
只能修饰常量
惰性值在赋值的时候并不触发,只有在要用到的时候才会触发
如上图,debug到第11行,a和b都是0,并不会触发a=1赋值,以及sum方法的运行
如下图,只有用到的时候才会触发赋值和方法调用
开始分析字节码实现:
代码
object Test1 {
def sum:Int = {
2
}
def main(args: Array[String]): Unit = {
lazy val a =1
lazy val b =sum
print("fasfd")
var c =a
var d =b
print("ok")
}
}
我们先看整个方法:我们多了四个方法
然后看下main方法:
很明显:
在
lazy val a =1
lazy val b =sum
的时候,只是 new了两个为0的 IntRef ----这块就不先展开了
真正的"赋值"/函数调用是发生在
var c =a
var d =b
的时候,这时候看上图的两条绿线,发生了对函数a$1 和b$1的调用
所以这就是所谓的懒加载:
我们再看:
lazy val b =sum对应的方法 b$1
b$1有三个局部变量
0:this指针
1:IntRef --就是在main方法开始执行 lazy val b =sum中 并不执行sum方法,而直接new的一个初始化中 IntRef(0)
2.一个VolitaleByteRef对象,值是0--后面再说,会用到
由于lazy无法修饰var变量 ,所以lazy只能修饰val 变量 ,所以 lazy 变量只需要被赋值一次就行了,绿框中的的判断条件,明显是如果其他地方调用赋值后,(这块是其他地方如果赋值了,会putfield改变VolitaleByteRef的elem,这样在 if_icmpne 时会跳到20---具体逻辑和线程安全看:)
我们在获取lazy 变量时就直接跳转到20步,直接通过 getfield 来获取 lazy变量的值了,不需要再重新赋值了
我们调用b$1其实是为了调用真正发生逻辑的b$Izycompyte$1方法:
这个方法如下:
整个方法都被sync锁着 ---就是sync(this) --因为dup复制了this指针
,所以我们惰性赋值是线程安全的,
然后在进入赋值前还是先判断一次惰性值是否被其他地方赋值过
如下图,在退出锁前,修改了VolitaleByteRef的elem,让其他引用到惰性变量的地方可以不用再赋值了
然后结合异常表
如上图我们在4-40步的时候发生任何异常和错误,都会解锁并抛出来
真正的赋值调用在:绿框中
a$1和对应的compute方法就不分析了
总结:
更清晰的总结:----从别的地方盗的图---不对应本文代码