-2

The below code generates OOM error while I am only appending to the existing StringBuilder. Please explain why OOM is generated as I am not creating any new object for each recursion.

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)
  }
4
  • stackoverflow.com/questions/2431040/… stackoverflow.com/questions/51227953/… Go through these two links. Commented Aug 4, 2019 at 5:11
  • 1
    The recursive recite() method has no exit. There's no way for it to terminate. Commented Aug 4, 2019 at 5:12
  • Please check your logic like nowhere you are comparing start and end value to terminate the recursive logic. Commented Aug 4, 2019 at 5:29
  • where are you calling this recite and with what values for start and end ? Commented Aug 4, 2019 at 7:38

2 Answers 2

2

Despite you only appending to a single StringBuilder, internally a new array will be allocated with larger capacity on the heap when current capacity is reached. Note how error message indicates call to 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)

There are two possible issues with your code. One is already explained by texasbruce regarding missing recursion exit point. The other might be regarding behaviour of count variable. Try initialising it to equal start value and then decrement it

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

instead of incrementing it

var count = 0
...
count += 1

In fact we can drop count variable all-together. Try the following example

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))
}

which outputs

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.

Note how we used start == end as exit condition.

Sign up to request clarification or add additional context in comments.

Comments

2

Your recursive function has no ending condition. You should end it when you redeem song to a string. Otherwise contrinue recursion:

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

1 Comment

An exception or error caused a run to abort: Java heap space java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:421) at java.lang.StringBuilder.append(StringBuilder.java:136) at scala.collection.mutable.StringBuilder.append(StringBuilder.scala:210) at BeerSong$.recite(BeerSong.scala:26)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.