首先:只要 JVM GC 认为有必要,就会实际释放未使用的内存。所以 scalac 对此无能为力。
scalac 唯一能做的就是设置对 null 的引用,不仅是在它们超出范围时,而且在它们不再使用时。
基本上
val huge: HugeObjectType
val derivative1 = huge.map(_.x)
huge = null // inserted by scalac
val derivative2 = derivative1.groupBy(....)
derivative1 = null // inserted by scalac
根据scala-internals 上的这个线程,它目前没有这样做,最新的热点 JVM 也没有提供打捞。请参阅 scalac 黑客 Grzegorz Kossakowski 的帖子和该线程的其余部分。
对于 JVM JIT 编译器正在优化的方法,JIT 编译器会尽快将引用设为空。但是,对于只执行一次的 main 方法,JVM 永远不会尝试对其进行完全优化。
上面链接的线程包含对该主题和所有权衡的非常详细的讨论。
请注意,在典型的大数据计算框架(例如 apache spark)中,您使用的值不是对数据的直接引用。所以在这些框架中,引用的生命周期通常不是问题。
对于上面给出的示例,所有中间值都只使用一次。因此,一个简单的解决方案是将所有中间结果定义为 defs。
def huge: HugeObjectType
def derivative1 = huge.map(_.x)
def derivative2 = derivative1.groupBy(....)
val result = derivative2.<some other transform>
一种不同但非常有效的方法是使用迭代器!map
像迭代器这样的链接函数filter
逐项处理它们,导致没有中间集合被物化..这非常适合场景!这对诸如此类的功能没有帮助,groupBy
但可能会显着减少前功能和类似功能的内存分配。上面提到的 Simon Schafer 的学分。