StringBuilder - java.lang.OutOfMemoryError: Java heap space

StringBuilder - java.lang.OutOfMemoryError: Java heap space

当我仅附加到现有的 StringBuilder 时,以下代码会产生 OOM 错误。请解释为什么会产生 OOM,因为我没有为每个递归创建任何新对象。

object BeerSong {

  val song = StringBuilder.newBuilder
  var count = 0
  def recite(start : Int, end : Int): String = {
      start match {
        case 1 => song.append(s"$start bottles of beer on the wall, $start bottles of beer.\nTake one down and pass it around, no more bottles of beer on the wall.\n")
        case 0 => song.append("No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, 99 bottles of beer on the wall.")
        case _ => song.append(s"$start bottles of beer on the wall, $start bottles of beer.\nTake one down and pass it around, ${start.toInt - 1} bottles of beer on the wall.\n")
      }
    count += 1
      if (count == end) {
            song.toString()
      }
      recite(start -1, end)
  }

你的递归函数没有结束条件。当您将歌曲兑换成字符串时,您应该结束它。否则继续递归:

  if (count >= end) {
        song.toString()
  }
  else {
        recite(start -1, end)
  }

尽管您只附加到单个 StringBuilder,但当达到当前容量时,内部将在堆上分配一个更大容量的新数组。请注意错误消息如何指示调用 Arrays.copyOf:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)

您的代码可能存在两个问题。 texasbruce 已经解释了一个关于缺少递归出口点的问题。另一个可能与 count 变量的行为有关。尝试将其初始化为等于 start 值,然后将其递减

val start = 100
var count = start
...
count -= 1

而不是增加它

var count = 0
...
count += 1

事实上我们可以把count变量放在一起。试试下面的例子

object Example extends App {
  val song = StringBuilder.newBuilder
  def recite(start : Int, end : Int): String = {
    start match {
      case 1 => song.append(s"$start bottles of beer on the wall, $start bottles of beer.\nTake one down and pass it around, no more bottles of beer on the wall.\n")
      case 0 => song.append("No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, 99 bottles of beer on the wall.")
      case _ => song.append(s"$start bottles of beer on the wall, $start bottles of beer.\nTake one down and pass it around, ${start.toInt - 1} bottles of beer on the wall.\n")
    }
    if (start == end) {
      song.toString()
    }
    else recite(start - 1, end)
  }

  println(recite(100, 0))
}

输出

100 bottles of beer on the wall, 100 bottles of beer.
Take one down and pass it around, 99 bottles of beer on the wall.
99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.
98 bottles of beer on the wall, 98 bottles of beer.
...
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottles of beer on the wall.
1 bottles of beer on the wall, 1 bottles of beer.
Take one down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, 99 bottles of beer on the wall.

请注意我们如何使用 start == end 作为退出条件。