Sunday 18 March 2018

Kotlin: tailrec - Permanent fix for Painful StackOverflowError



Parameters and local variables are allocated on the stack (with reference types the object lives on the heap and a variable references that object). The stack typically lives at the upper end of your address space and as it is used up it heads towards the bottom of the address space (i.e. towards zero).

Your process also has a heap, which lives at the bottom end of your process. As you allocate memory, this heap can grow towards the upper end of your address space. As you can see, there is a potential for the heap to "collide" with the stack (a bit like tectonic plates!!!).

The common cause for a stack overflow is a bad recursive call. Typically, this is caused when your recursive functions doesn't have the correct termination condition, so it ends up calling itself forever. However, with GUI programming, it's possible to generate indirect recursion. For example, your app may be handling paint messages, and, whilst processing them, it may call a function that causes the system to send another paint message. Here you've not explicitly called yourself, but the OS/VM has done it for you.

To deal with them you'll need to examine your code. If you've got functions that call themselves then check that you've got a terminating condition. If you have then check than when calling the function you have at least modified one of the arguments, otherwise there'll be no visible change for the recursively called function and the terminating condition is useless.

If you've got no obvious recursive functions then check to see if you're calling any library functions that indirectly will cause your function to be called (like the implicit case above).

Courtesy: Link1

Example:  This is a demo example which i have used to get the StackOverflowError
Wrote a long run recursive method to sum up-to n numbers.

// Recursive function to find sum of numbers
fun sumOfNumbers(aVal:Int):Int {
    sum+=aVal;
    if (aVal == 0)
        return sum  
    else      
        return sumOfNumbers(aVal-1)
}
print(sumOfNumbers(3)) // 3+2+1=6  - This won't cause StackOverflowError
Output: 6

See, here we have used the smaller number, assume i trying to give a larger number (it may differ based on the system memory), While I'm testing with the below scenario got caught with the StackOverflowError at 1023...1050 (randomly).

print(sumOfNumbers(9000))  // 9000+8999+8998...  - This cause StackOverflowError

Hence, In order to avoid this kind of issue, kotlin provides an option called tailrec, we need to add this keyword before to the recursive method, the rest will taken care by the kotlin internally do the looping kind of approach and facilitates to provide us the solid & valid output with out any crash or error.

// Same Recursive function to find sum of numbers using tailrec 
tailrec fun sumOfNumbers(aVal:Int):Int {
    sum+=aVal;  
    if (aVal == 0)
        return sum  
    else      
        return sumOfNumbers(aVal-1)
}

print(sumOfNumbers(9000))   // The tailrec keyword take care of the app crash or SOF error.
Output: 040504500

No comments: