Scala shell 中的垃圾收集

Posted

技术标签:

【中文标题】Scala shell 中的垃圾收集【英文标题】:Garbage collection in the Scala shell 【发布时间】:2016-11-11 04:11:57 【问题描述】:

所以我刚刚开始使用 Scala,并使用以下代码创建名为 out 的虚拟数据的 IndexedSeq。虚拟数据由 20000 个元组组成,每个元组包含一个 36 个字符的唯一标识符和一个包含 1000 个浮点数的列表。

import scala.util.Random

def uuid = java.util.UUID.randomUUID.toString

def generateRandomList(size: Int): List[Float] = 
    List.fill(size)(Random.nextFloat)


val numDimensions = 1000
val numberToWrite = 20000

val out = for ( i <- 1 to numberToWrite) yield 
      val randomList = generateRandomList(numDimensions)
      (uuid, randomList)  // trying tuples insread

但是当我运行最后一条语句时(只需复制并粘贴到 Scala shell 中),我收到以下错误:

java.lang.OutOfMemoryError: GC overhead limit exceeded
  at java.lang.Float.valueOf(Float.java:433)
  at scala.runtime.BoxesRunTime.boxToFloat(BoxesRunTime.java:73)
  at $anonfun$generateRandomArray$1.apply(<console>:14)
  at scala.collection.generic.GenTraversableFactory.fill(GenTraversableFactory.scala:90)
  at .generateRandomArray(<console>:14)
  at $anonfun$1.apply(<console>:17)
  at $anonfun$1.apply(<console>:16)
  at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
  at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
  at scala.collection.immutable.Range.foreach(Range.scala:160)
  at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
  at scala.collection.AbstractTraversable.map(Traversable.scala:104)
  ... 20 elided

这被解释为当我的大部分时间都花在垃圾收集 (GC) [1] 上时发生的 Java 异常。

根据 [2],一个 36 字符的字符串大约需要 112 个字节。浮点数占用 4 个字节。我的内部列表中有 1000 个,总共大约 4000 个字节。所以忽略列表和元组开销,那么我的out IndexedSeq 的每个元素将大约是 ~4200 字节。因此,拥有 20000 意味着总共约 84e6 个字节。

考虑到这一点,在异常之后我运行了这个(取自 [3]):

scala> val heapSize = Runtime.getRuntime().totalMemory(); // Get current size of heap in bytes
heapSize: Long = 212860928

scala> val heapMaxSize = Runtime.getRuntime().maxMemory(); // Get maximum size of heap in bytes. The heap cannot     grow beyond this size.// Any attempt will result in an OutOfMemoryException.

heapMaxSize: Long = 239075328

scala> val heapFreeSize = Runtime.getRuntime().freeMemory();  // Get amount of free memory within the heap in bytes.     This size will increase // after garbage collection and decrease as new objects are created.

heapFreeSize: Long = 152842176

虽然我的最大可用堆大小似乎大于我认为我需要的粗略内存量,但我尝试通过./scala -J-Xmx2g 增加堆大小 ([4])。虽然这解决了我的问题,但很高兴知道是否有更好的方法来创建这些随机数据,从而避免我不得不增加 JVM 可用的内存? 因此,我有这三个问题,如果有人能回答我将不胜感激:

    Scala 中的垃圾收集何时发生,尤其是 Scala shell?在我上面的命令中,可以收集什么,为什么要调用 GC(抱歉,第二部分可能表明我对 GC 缺乏了解)?

    我对占用的内存量的粗略计算是否有效(当然,我预计列表和元组的开销会更多,但假设相对没有那么多)?如果是这样,当我的最大堆大小(239e6 字节)应该涵盖这个时,为什么我会用完内存?如果不是,我在使用什么额外的内存?

    有没有更好的方法来为此创建随机数据?对于上下文,我试图创建一些可以并行化到 Spark 中的虚拟数据(使用 sc.parallelize),然后使用。 (所以当我开始在 Spark 中尝试它时,为了让它工作,我通过在我的 spark conf 中设置 spark.driver.memory 2g 而不是上面的 -J-Xmx2g 命令来增加驱动程序内存)。

感谢您的帮助!

链接

    Error java.lang.OutOfMemoryError: GC overhead limit exceeded How much memory does a string use in Java 8? How to view the current heap size that an application is using? Increase JVM heap size for Scala?

【问题讨论】:

+1 个很好的问题,背后有很多有用的研究;很难相信它没有被投票,而且它只有一个答案 【参考方案1】:

回答 REPL 特定部分:

https://issues.scala-lang.org/browse/SI-4331

进行大分配的人通常更喜欢ArrayBuffer

请注意,List 中存在开销,包括对原始值进行装箱。

JVM 堆在池中进行管理,您可以相对于彼此调整大小。但一般来说:

scala> var x = new Array[Byte](20000000 * 4)
x: Array[Byte] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
scala> x = null
x: Array[Byte] = null

scala> x = new Array[Byte](20000000 * 4)
x: Array[Byte] = [B@475530b9

scala> x = null
x: Array[Byte] = null

scala> x = new Array[Byte](20000000 * 4)
java.lang.OutOfMemoryError: Java heap space
  ... 32 elided

【讨论】:

在风格上更喜欢 Array.ofDim[Byte](10)Array.fill(10)(1: Byte)

以上是关于Scala shell 中的垃圾收集的主要内容,如果未能解决你的问题,请参考以下文章

Java 中的垃圾收集器

JVM中的垃圾回收器及垃圾收集算法描述

67.Java垃圾收集机制对象引用垃圾对象的判定垃圾收集算法标记—清除算法标记—整理算法分代收集垃圾收集器性能调优

标记为垃圾收集vs符合java中的垃圾收集条件

JVM垃圾收集器

JVM中的垃圾收集器