by 十三
背景:
近日遇到個奇怪的問題:在沒有明顯循環(huán)或遞歸的代碼地方抚笔,日志中卻拋出了stackoverflow的錯誤扶认,由于代碼入口處是調(diào)用了configuration獲取yarn的配置,所以剛開始定位思路有點跑偏了殊橙,后來就嘗試去掉了lazy關(guān)鍵詞辐宾,發(fā)現(xiàn)錯誤沒有了,根據(jù)后續(xù)的日志打印發(fā)現(xiàn)蛀柴,跟預(yù)期有出入螃概,才發(fā)現(xiàn)是因為log中使用了錯誤的變量(看來是寫代碼太不走心了,走查代碼也沒走心鸽疾,??),至此才發(fā)現(xiàn)問題根因训貌。
原因:
我們先來看如下兩段代碼的區(qū)別
片段1:
lazy val upperLimit: Int = {
val increment: Int = 512//最初是conf.getSomeValue方法制肮,方便起見賦初值
val containerMaxMem: Int = 20//最初是conf.getSomeValue方法冒窍,方便起見賦初值
val upper = (containerMaxMem - math.ceil(math.max(containerMaxMem * 0.1 * 1024, 384)) / increment * increment / 1024.0).toInt
log.debug(s"upper = $upperLimit")
upper
}
println(upperLimit)
片段2:
val upperLimit: Int = {
val increment: Int = 512
val containerMaxMem: Int = 20
val upper = (containerMaxMem - math.ceil(math.max(containerMaxMem * 0.1 * 1024, 384)) / increment * increment / 1024.0).toInt
log.debug(s"upper = $upperLimit")
upper
}
println(upperLimit)
從代碼的角度來看,唯一的區(qū)別在于前者多了一個lazy豺鼻,那么兩段代碼的輸出會有什么差別嗎综液?
我們來看看輸出結(jié)果:
片段1的輸出:
An exception or error caused a run to abort.
java.lang.StackOverflowError
at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.write(PrintStream.java:526)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:823)
at scala.Console$.println(Console.scala:148)
at scala.Predef$.println(Predef.scala:315)
片段2的輸出:
0
18
片段1中由于加了lazy關(guān)鍵詞,所以在log中首次使用的時候儒飒,才會去真正計算它的值谬莹,在沒有得到返回值之前,log中又開始計算值桩了,所以不斷地壓棧附帽,最終導(dǎo)致棧溢出的錯誤。
而片段2中井誉,去掉了lazy關(guān)鍵詞蕉扮,log中首次使用的時候,就會使用Int類型的初始值颗圣,也就是0.
最后再看一看
scala> lazy val a = 1
a: Int = <lazy>
scala> a
res1: Int = 1
scala> val a = 1
a: Int = 1
lazy雖好喳钟,使用還得謹(jǐn)慎。