Какие операции могут (не) бросать StackOverflowError?

Когда будет выбрано StackOverError?

Или, вернее, когда он будет не?

Для example, если мы используем примитивные операторы +, +=, -, -=, == <, >, /, % и т.д.:

try {
     // operations +, +=, -, -=, == <, >, /, %, etc
} catch (java.lang.StackOverflowError e) {
     // will never occur?
}

Есть ли гарантия, что StackOverflowError не будет выбрано?

Ответ 1

верно ли, что код, который не вызывает никаких функций, никогда не будет бросать java.lang.StackOverflowError?

A StackOverflowError is-a VirtualMachineError. Выяснилось, что нет никаких ограничений на VirtualMachineError s. Спецификация виртуальной машины Java говорит следующее (выделено мной).

Эта спецификация не может предсказать, где внутренние ошибки или ограничения ресурсов могут встречаться и не указывать точно, когда они могут быть сообщены. Таким образом, любой из подклассов VirtualMachineError, определенных ниже, может быть сброшен в в любое время во время работы виртуальной машины Java:... StackOverflowError

Ответ 2

В теории, на каком-то странном JVM + оператор может быть реализован с использованием рекурсии внутри, добавив + 1 в рекурсивный цикл. Другие операторы также могут быть реализованы с использованием внутреннего вызова метода.

Я не думаю, что это будет так. В каждой типичной JVM/архитектуре эти операции выполняются с использованием единой операции/инструкции. Для всех этих операторов есть инструкции по байт-коду, и я ожидаю, что они будут переведены на сборку 1:1. Но в JLS нет никаких гарантий.

Кстати, JavaDoc:

Брошено, когда происходит переполнение стека, потому что приложение слишком сильно перегружается.

не совсем корректно. Вы можете получить StackOverflowError без какой-либо рекурсии - если у вас очень глубокое дерево вызовов, а методы имеют очень длинный список аргументов. Однако это очень сложно достичь на практике.

Ответ 3

Единственная ссылка на StackOverflowError в Спецификации Java Language следующая:

15.12.4.5 Создание кадра, синхронизация, управление передачей

Метод m в некотором классе S был идентифицирован как вызываемый.

Теперь создается новый кадр активации, содержащий целевую ссылку (если есть) и значения аргумента (если есть), а также достаточно места для локальных переменных и стека для вызываемого метода и любой другой информации бухгалтерского учета которые могут потребоваться при реализации [...]. Если для создания такого кадра активации недостаточно памяти, генерируется StackOverflowError.

Спецификация JVM говорит следующее:

StackOverflowError: В реализации виртуальной машины Java не хватает пространства стека для потока, , как правило, потому что поток выполняет неограниченное количество рекурсивных вызовов в результате ошибки в исполняемой программе.

Итак, судя по приведенным выше утверждениям...

Мне было интересно, правда ли, что код, который не вызывает никаких функций, никогда не будет бросать java.lang.StackOverflowError?

... да, это правда.

Например, если я использую примитивы (t25 > , +=, -, -=, ==, <, >, /, % и т.д. включая длинные и двойные),

Right. Ни один из них никогда (сам по себе) не вызывает метод и поэтому не должен вызывать StackOverflowError.

Ответ 4

См. документацию:

Брошено, когда происходит переполнение стека, потому что приложение слишком сильно перегружается.

Итак, когда ваше приложение вообще не рекурсивно (т.е. не вызывает методы), вы не получите StackOverflowError.

Конечно, когда мы не говорим только о примитивах (где базовые операции реализуются непосредственно с помощью Java bytecode инструкции), мы легко запускаем в StackOverflowError s.

Пример:

int foo = 23;

foo = 23 + bar;

Что делать, если bar является java.lang.Integer? Java сделает автоматическое unboxing, и это приведет к следующему байт-коду:

   0:   bipush  23
   2:   istore_1
   3:   bipush  23
   5:   getstatic   #3; //Field bar:Ljava/lang/Integer;
        v v v v v v v 
   8:   invokevirtual   #4; //Method java/lang/Integer.intValue:()I
        ^ ^ ^ ^ ^ ^ ^
   11:  iadd
   12:  istore_1
   13:  return

Это (неявный) метод invokation и поэтому может вызвать StackOverflow.

Ответ 5

Например, если я использую операторы +, +=, -, -=, == <, >, /, % и т.д. на примитивах (включая long и double)... Можем ли мы гарантировать, что из этой операции не будет выбрана ошибка stackoverflow?

Да, для стандартных операторов математики и сравнения. Ключ был там, где вы сказали "... на примитивах..." Поскольку Java не разрешает перегрузку оператора, а реализация JVM для примитивов не будет включать рекурсию, вы можете быть уверены. Это не обязательно было бы, если бы вы говорили о непримитивах, когда некоторые из этих операторов могли вызвать вызов кода, отличного от JVM (с помощью +, например, для добавления объекта к строке, объект toString).