Scala中的大型CSV读取超出了GC开销限制

Scala中的大型CSV读取超出了GC开销限制,scala,garbage-collection,jvm,sbt,Scala,Garbage Collection,Jvm,Sbt,所以。我使用的是Scala,我对它比较陌生,主要是python方面的。我正在通过sbt编译和运行我的代码。我在一个Ubuntu设备上,目前正在运行Java6。我有两个CSV;我需要接受它们,处理它们,然后操纵它们。每个CSV约为250mb;如果这样做有效,我可能会用更大的CSV重复这个过程 我定义了一个函数,它读取CSV并将每一行写入我需要的数据结构。我对系列中的每个CSV调用此函数。问题是:对于第一个CSV,它返回得非常完美且非常快,但第二个CSV总是抛出java.lang.OutOfMemo

所以。我使用的是Scala,我对它比较陌生,主要是python方面的。我正在通过sbt编译和运行我的代码。我在一个Ubuntu设备上,目前正在运行Java6。我有两个CSV;我需要接受它们,处理它们,然后操纵它们。每个CSV约为250mb;如果这样做有效,我可能会用更大的CSV重复这个过程

我定义了一个函数,它读取CSV并将每一行写入我需要的数据结构。我对系列中的每个CSV调用此函数。问题是:对于第一个CSV,它返回得非常完美且非常快,但第二个CSV总是抛出java.lang.OutOfMemoryError:GC开销限制超出错误

我尝试了很多事情。My build.sbt定义javaOptions+=-Xmx20480m-XX:+HeapDumpOnOutOfMemoryError;我也尝试过使用-XX:-usegcoveredlimit,但这似乎没有任何帮助。根据我读过的Java文档,这个错误表明大量的系统资源被花费在垃圾收集上——但坦率地说,我不清楚什么是垃圾收集,或者如何减少垃圾收集。我想我的功能一定是。。。内存在某处泄漏,或者我一定是误用了Scala,但我不知道怎么做

以下是我的功能:

def readAndProcessData(path: String) = {
    val fileLines = Source.fromFile(path).getLines.drop(1)
    val ret = mutable.Map[String, List[Tuple2[String, String]]]()

    def addRowToRet(row: String) = {
        val rowArray = row.split(",")
        if (!(ret contains rowArray(0))) {
            ret.update(rowArray(0), List[Tuple2[String, String]]())
        }
        ret(rowArray(0)) = Tuple2(rowArray(1), rowArray(2)) :: ret(rowArray(0))
    }

    for (row <- fileLines) {
        addRowToRet(row)
    }

    ret.map{tup => (tup._1 -> tup._2.sorted)}

}

谢谢

尝试返回ret并用包装方法映射它。这应该避免将所有内容都放在内存中。

尝试返回ret并用包装方法映射它。这应该避免将所有内容都放在内存中。

首先,如果您不打算运行fork,或者提高sbt的内存限制,并删除javaOptions设置。分叉在这里可能是一个好主意,因此您不会将程序的内存使用行为与sbt的内存使用行为混为一谈

您还应该关闭正在创建的源对象,以确保释放其资源

它是否在一致的位置崩溃,例如在排序时?还是崩溃发生在代码中相当随机的点上

我假设您正在读取的文件采用ASCII或UTF8等编码,其中大部分字符都用8位表示。Java每个字符使用16位,因此请记住,通过将其读入Java字符串,您的大小比其他开销要大一倍多。这本身不应该把你推到一边,但这意味着当你加载了两个250MB的文件时,你可能会为数据消耗超过1GB的内存

相对于文件中的行数,密钥的分布情况如何?换句话说,你的地图上是否有几乎每一行的条目,大约一半的行,四分之一的行,等等?就条目而言,您可能有一个相当大的映射,当您对其执行映射操作以对值进行排序时,您将在内存中最终得到其中两个值,直到函数返回并且旧的值变得可收集为止。您可能还想尝试使用不可变映射或围绕Java可变映射的包装器。有时Scala的可变数据结构不如其不变的对应结构那么健壮

而且,我从来没有在scala.io.Source上交过好运。如果在您相当确定确实分配了足够的内存后,它仍然失败,那么您可能希望尝试使用Java的IO库


最后,如果检查一些设置并稍微戳一下不起作用,您应该将内存分析器连接到它,例如。那就是你有机会找出你真正的问题所在,而不是通过修改来猜测和检查。

首先,如果你不打算运行,或者增加sbt的内存限制,并删除javaOptions设置。分叉在这里可能是一个好主意,因此您不会将程序的内存使用行为与sbt的内存使用行为混为一谈

您还应该关闭正在创建的源对象,以确保释放其资源

它是否在一致的位置崩溃,例如在排序时?还是崩溃发生在代码中相当随机的点上

我假设您正在读取的文件采用ASCII或UTF8等编码,其中大部分字符都用8位表示。Java每个字符使用16位,因此请记住,通过将其读入Java字符串,您的大小比其他开销要大一倍多。这本身不应该把你推到一边,但这意味着当你加载了两个250MB的文件时,你可能会为数据消耗超过1GB的内存

相对于文件中的行数,密钥的分布情况如何?换句话说,你的地图上是否有几乎每一行的条目,大约一半的行,四分之一的行,等等?就条目而言,您可能有一个相当大的映射,当您对其执行映射操作以对值进行排序时,您将在内存中最终得到其中两个值,直到函数返回并且旧的值变得可收集为止。您可能还想尝试使用不可变映射或包装器 围绕Java可变映射。有时Scala的可变数据结构不如其不变的对应结构那么健壮

而且,我从来没有在scala.io.Source上交过好运。如果在您相当确定确实分配了足够的内存后,它仍然失败,那么您可能希望尝试使用Java的IO库


最后,如果检查一些设置并稍微戳一下不起作用,您应该将内存分析器连接到它,例如。那就是你有机会找出你真正的问题所在,而不是通过修改来猜测和检查。

使用for循环看起来不像是Scala'ish。1.使用filelines.foldLeft将csv数据转换为映射。2.尝试让AddoRoReTET递归,如果您仍然想使用相同的代码尝试使用RET.PAR。MAP{TUP= > TUP。javaOptions仅用于sbt派生一个新进程来运行您的程序,这不是默认行为。@Rajesh-是的,for循环令人不快;我一直在想如何移除它。谢谢foldLeft的建议-我们会考虑的!我还没有考虑过将这一类并行化,但这并不是事情第二次通过时令人窒息的步骤,唉。谢谢@ErikEngbrecht哦,伙计,我不知道关于SBT的事。我现在要去读一下Forking,这可以解释很多东西。使用for循环看起来不像Scala'ish。1.使用filelines.foldLeft将csv数据转换为映射。2.尝试让AddoRoReTET递归,如果您仍然想使用相同的代码尝试使用RET.PAR。MAP{TUP= > TUP。javaOptions仅用于sbt派生一个新进程来运行您的程序,这不是默认行为。@Rajesh-是的,for循环令人不快;我一直在想如何移除它。谢谢foldLeft的建议-我们会考虑的!我还没有考虑过将这一类并行化,但这并不是事情第二次通过时令人窒息的步骤,唉。谢谢@ErikEngbrecht哦,伙计,我不知道关于SBT的事。我现在就要去读关于分叉的书——这可以解释很多东西。你关于启用分叉的建议似乎已经基本上立即解决了这个问题!非常感谢你花时间在这方面——你也给了我很多其他非常好的信息,非常经典。到目前为止,在我的跑步中启用分叉已经解决了所有问题-太棒了!您关于启用分叉的建议似乎已经基本上立即解决了问题!非常感谢你花时间在这方面——你也给了我很多其他非常好的信息,非常经典。到目前为止,在我的跑步中启用分叉已经解决了所有问题-太棒了!