从 Map 获取值时的 Scala StackOverflowError
Scala StackOverflowError when getting value from Map
经过一些重构后,我们突然发现在运行时发生了这种情况:
java.lang.WhosebugError: null
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
我们发现了类似的问题,但其中 none 个问题恰好是这样的:
- Whosebug with map.get
- Scala Map.mapValues Whosebugerror
提到的问题指向 MapLike.mapValues
的懒惰,经过进一步研究,我们找到了原因。
我们有一些清理代码会定期调用并执行如下操作:
case class EvictableValue(value: String, evictionTime: Instant)
val startData = Map(
"node1" -> Map(
"test" -> EvictableValue("bar", Instant.now().plusSeconds(1000))
)
)
// every n seconds we do the below code
// here simulated by the fold
val newData = (1 to 20000).foldLeft(startData){(data, _) =>
data.mapValues { value =>
value.filter(_._2.evictionTime.isBefore(Instant.now()))
}
}
// this stack overflows
val result = newData.get("test")
解决方案是切换到 Map.transform
data.transform { (_, value) =>
value.filter(_._2.evictionTime.isBefore(Instant.now()))
}
或按说明强制查看 here
data.mapValues{ value =>
value.filter(_._2.evictionTime.isBefore(Instant.now()))
}.view.force
经过一些重构后,我们突然发现在运行时发生了这种情况:
java.lang.WhosebugError: null
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
at scala.collection.MapLike$MappedValues.get(MapLike.scala:249)
我们发现了类似的问题,但其中 none 个问题恰好是这样的:
- Whosebug with map.get
- Scala Map.mapValues Whosebugerror
提到的问题指向 MapLike.mapValues
的懒惰,经过进一步研究,我们找到了原因。
我们有一些清理代码会定期调用并执行如下操作:
case class EvictableValue(value: String, evictionTime: Instant)
val startData = Map(
"node1" -> Map(
"test" -> EvictableValue("bar", Instant.now().plusSeconds(1000))
)
)
// every n seconds we do the below code
// here simulated by the fold
val newData = (1 to 20000).foldLeft(startData){(data, _) =>
data.mapValues { value =>
value.filter(_._2.evictionTime.isBefore(Instant.now()))
}
}
// this stack overflows
val result = newData.get("test")
解决方案是切换到 Map.transform
data.transform { (_, value) =>
value.filter(_._2.evictionTime.isBefore(Instant.now()))
}
或按说明强制查看 here
data.mapValues{ value =>
value.filter(_._2.evictionTime.isBefore(Instant.now()))
}.view.force