针对不变性的Scala编译器优化
scala编译器是否通过将refs移除到块中仅使用一次的val来优化内存使用?
想象一个对象保存一些巨大的数据 – 达到克隆数据或其衍生物可能会刮掉JVM /机器的最大内存量的大小. 一个最小的代码示例,但想象一下更长的数据转换链: val huge: HugeObjectType val derivative1 = huge.map(_.x) val derivative2 = derivative1.groupBy(....) 编译器是否例如计算衍生物1后,留下大量有资格进行垃圾收集的情况?或者它会保持活着,直到包裹块退出? 理论上不变性很好,我个人觉得它上瘾.但是要适应那些无法在当前操作系统上逐项进行流处理的大数据对象 – 我认为它本身就具有与合理内存利用率不匹配的阻抗,因为JVM上的大数据应用程序不是不,编译器会针对这种情况进行优化. 解决方法
首先:只要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上的this thread,它目前没有这样做,最新的热点JVM也没有提供打捞.请参阅scalac hacker Grzegorz Kossakowski的帖子和该帖子的其余部分. 对于由JVM JIT编译器优化的方法,JIT编译器将尽快使引用为空.但是,对于仅执行一次的主方法,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的积分. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |