2015-11-25 3 views
0

Для проверки того, как глубокая рекурсия в Scala может проникать глубоко на мою машину, была написана следующая программа. Я предполагаю, что в основном это зависит от размера стека, назначенного этой программе. Однако, после вычисления максимальной глубины рекурсии (исключения catching, когда она появляется) и попытки имитировать рекурсию этой глубины, я получил нечетные выходы.Разница глубины рекурсии Scala и StackOverflowError ниже допустимой глубины

object RecursionTest { 

    def sumArrayRec(elems: Array[Int]) = { 

    def goFrom(i: Int, size: Int, elms: Array[Int]): Int = { 
     if (i < size) elms(i) + goFrom(i + 1, size, elms) 
     else 0 
    } 
    goFrom(0, elems.length, elems) 
    } 

    def main(args: Array[String]) { 
    val recDepth = recurseTest(0, 0, Array(1)) 
    println("sumArrayRec = " + sumArrayRec((0 until recDepth).toArray)) 
    } 

    def recurseTest(i: Int, j: Int, arr: Array[Int]): Int = { 
    try { 
     recurseTest(i + 1, j + 1, arr) 
    } catch { case e: java.lang.StackOverflowError => 
     println("Recursion depth on this system is " + i + ".") 
     i 
    } 
    } 

} 

Результаты варьируются в зависимости от исполнения программы.

Одним из них является желательным выход:

/usr/lib/jvm/java-8-oracle/bin/java 

Recursion depth on this system is 7166. 
sumArrayRec = 25672195 

Process finished with exit code 0 

Тем не менее, второй возможный выход, который я получаю указывает на ошибку:

/usr/lib/jvm/java-8-oracle/bin/java 

Recursion depth on this system is 8129. 
Exception in thread "main" java.lang.StackOverflowError 
    at RecursionTest$.goFrom$1(lab44.scala:6) 
    at RecursionTest$.goFrom$1(lab44.scala:6) 
    at RecursionTest$.goFrom$1(lab44.scala:6) 
    (...) 
    at RecursionTest$.goFrom$1(lab44.scala:6) 
    at RecursionTest$.goFrom$1(lab44.scala:6) 

Process finished with exit code 1 

я не наблюдал никакой зависимости или отношения их , Я только что получил первый, а второй раз

Все это привело меня к следующим вопросам:

  1. Зачем возникает ошибка и что вызывает ее?
  2. Это ошибка переполнения стека, и если да ...
  3. Почему размер стека изменяется в то же время, когда программа работает? Возможно ли это?

Когда я изменил println("sumArrayRec = " + sumArrayRec((0 until recDepth).toArray)) в println("sumArrayRec = " + sumArrayRec((0 until recDepth - 5000).toArray)), так гораздо меньше, поведение остается тем же самым.

ответ

1
  1. Вы получаете переполнение стека, потому что вызывает переполнение стека
  2. Да
  3. Максимальный стек не меняется, виртуальная машина устанавливает это. Ваши два метода: recurseTest и sumArrayRec нажимают разные суммы в стек. recurseTestв основном добавление 2 ints стека с каждым вызовом, в то время как sumArrayRec добавляет 3 ints, так как вы звоните elms(i), возможно, больше с добавлением/ifelse (дополнительные инструкции). Сложно сказать, поскольку JVM справляется со всем этим, и цель заключается в том, чтобы скрыть это от того, что вам нужно знать. Кроме того, кто знает, какие оптимизации JVM делает за кулисами, которые могут радикально повлиять на размеры стека, созданные при каждом вызове метода. На нескольких прогонах вы получите разную глубину для стека из-за системного времени и т. Д., Возможно, jvm будет оптимизирован за короткое время, когда программа запустится, может быть, это не так, это не детерминировано в этом сценарии. Если вы запустили некоторый код разминки перед рукой, это может сделать ваши тесты более детерминированными, поэтому любая оптимизация будет или не состоится.

Вы должны изучить что-то вроде JMH для своих тестов, это поможет с детерминизмом.

Боковое примечание: вы также можете вручную изменить размер стека с помощью -Xss2M, который используется для использования SBT.

+0

Почему вы сказали, что recurseTest нажимает только два параметра, так как он получает три параметра? –

+0

, поэтому он создает 2 новых ints, 2 ints отправляются на него и один указатель, поэтому у вас есть около 5 ints в стеке, исключая инструкции, поэтому массив может быть оптимизирован, так как он не используется.На самом деле вы не можете сказать, если не посмотрите на сгенерированную сборку, -XX: + UnlockDiagnosticVMOptions -XX: + PrintAssembly', но оптимизация JVM может изменить их во время выполнения – Noah

Смежные вопросы