Размер стека вызовов метода вычислений для проверки StackOverflowException

Сегодня утром я ответил на вопрос, связанный с StackoverflowException. Человек спросил, когда возникает исключение Stackoverflow.

См. ссылку Простейшие способы вызвать переполнение стека в С#, С++ и Java

Итак, мой вопрос заключается в том, что есть какой-то метод, по которому мы можем вычислить размер стека вызовов метода динамически в нашей программе, а затем применить проверку перед вызовом метода, который проверяет, имеет ли стек вызовов метода пространство для его размещения или нет для предотвращения StackOverflowException.

Поскольку я человек java, я ищу Java, но также ищу объяснения, связанные с понятием без ограничения какого-либо языка программирования.

Ответ 1

Общая память, доступная JVM, составляет около 2-4 ГБ для 32-битной JVM и квадрат для 64-битной JVM (около 4-16EB). JVM разбивает память на:

  • Память кучи (распределение управляется с помощью параметров JVM -Xms и -Xmx)

    • построенные экземпляры объектов и массивов
    • статические классы и данные массива (включая экземпляры объектов/массивов)
    • экземпляры потоков (экземпляры объектов, данные времени выполнения и метаданные, включая ссылки на блокировку монитора потока)
  • Память без памяти

    • совокупная память стека
      • в потоковой памяти стека (распределение по потокам, управляемое с помощью опции JVM -Xss): кадры метода, аргументы, возвращаемые значения, локально объявленные примитивы и ссылки на объекты
    • статические константы (примитивы)
    • Пул экземпляров String
    • код java: загруженные классы и метаданные
    • Внутренняя память JVM (код JVM и структуры данных)

См. http://docs.oracle.com/javase/7/docs/api/java/lang/management/MemoryMXBean.html и http://www.yourkit.com/docs/kb/sizes.jsp

Есть ли какой-либо метод, по которому мы можем вычислить размер стека вызовов метода динамически в нашей программе

  • В Java SE/Java EE нет стандартного метода для получения фактического использования памяти потокового потока.
  • Существуют стандартные методы для получения совокупной памяти без кучи: MemoryMxBean.getNonHeapMemoryUsage(). Ссылаясь на это, вы не можете принимать динамические решения в коде, чтобы избежать исключения StackOverflow
  • Существуют стандартные методы для получения стека вызовов без использования памяти: Thread.getStackTrace() ThreadMxBean.getThreadInfo() и ThreadInfo.getStackTrace()

Я рекомендую вам не делать то, что вы предлагаете в вопросе, потому что:

  • Вы не можете сделать это без какого-либо сложного JVM-специфического API, который использует инструменты/интроспекты для использования динамической памяти стека потоков - где вы найдете такой API?
  • В потоке с потоком обычно потребляется крошечный объем памяти по сравнению со всей JVM, поэтому обычно легко назначить достаточно, чтобы соответствовать вашему алгоритму (например, по умолчанию размер стека размером 128 КБ для Windows 64bit JVM, в то время как 2 ГБ памяти может быть заложена в бюджете для всей JVM)
  • Это было бы очень ограничено по мощности: если ваша логика действительно требовала вызова метода, но вы не могли из-за недостаточной памяти, тогда ваша программа была бы сломана в этот момент. Исключение StackOverflow на самом деле будет лучшим ответом.
  • То, что вы пытаетесь сделать, может быть anti-design anti-pattern.
    "Правильный" подход заключался бы в определении требований к программе, определении требуемой среды выполнения (включая минимальную/необходимую память!) И разработке вашей программы соответственно для оптимальной производительности и использования памяти.

    Анти-шаблон - это не думать об этих вещах надлежащим образом во время проектирования и разработки, и просто представьте себе, что магия интроспекции времени выполнения может охватывать это. Могут существовать некоторые (редкие!) Приложения с высокой производительностью, требующие радикальной перестройки алгоритма во время выполнения, чтобы точно соответствовать обнаруженным ресурсам - но это сложно, уродливо и дорого.

    И даже тогда, вероятно, лучше было бы изменить динамические изменения динамического алгоритма на макроуровне от параметра -Xss, а не на микроуровне от точного потребления памяти стека в месте в коде.

Ответ 2

Надеюсь, я догадываюсь, что вы действительно спрашиваете. Сначала я подумал, что вы спрашиваете, сколько звонков вам больше подходит. Другими словами, я думал, что вы хотите знать, насколько вероятно, что вы должны вызвать это исключение, исходя из ваших текущих методов. Затем я решил, что вы действительно хотите узнать, сколько стека вам нужно играть. В этом случае есть еще один вопрос, который, как представляется, касается этого. Какова максимальная глубина стека вызовов java?

Это говорит вам, как установить это как параметр командной строки java (для java, а не для вашей программы).

В любом случае, я хотел бы указать, что переполнение стека в основном произошло со мной, когда у меня была бесконечная рекурсия. Я написал методы (по ошибке, конечно), которые называли себя, и должны были остановиться, когда проблема была решена, но каким-то образом условие завершения не было достигнуто. Это вызывает вызов метода в стек много раз, пока не будет превышен максимум. Не то, что я имел в виду.

Я надеюсь, что это поможет.

Ответ 3

Насколько мне известно, ограничение стека в Java довольно абстрактно и не предназначено для измерения. На самом деле, я подозреваю, что размер стека будет варьироваться от машины к машине на основе нескольких факторов, таких как память.

Я никогда не получал программу для выброса исключения, за исключением бесконечных циклов/рекурсии. Я почесываю голову, пытаясь понять, как можно было бы даже исключить исключение без бесконечного цикла. Если ваша программа вызывает много методов, то она, вероятно, создает объекты одновременно, и вы гораздо чаще получаете ошибку OutOfMemory, чем исключение без бесконечного цикла.

На самом деле, какой черт был бы точкой лимита стека, который мог бы ограничить вашу способность функционировать должным образом? У Java есть ограничения по памяти, чтобы позаботиться о том, чтобы вы перешли за борт с ресурсами. Цель состоит в том, чтобы поймать петли/рекурсию, которые запускались amok и должны быть пойманы.

Точка, которую я пытаюсь сделать, заключается в следующем: если исключения отражают тестирование вашего устройства, вы должны проверить эти циклы/рекурсивные функции для какого-то неподконтрольного поведения. Стек вызовов очень, очень длинный, и я сомневаюсь, что вы достигли его естественным образом.

Ответ 4

Я думаю, вы можете использовать StackTrace для получения размера стека вызовов метода, как показано ниже

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();

Ответ 5

Ну, вы можете использовать что-то вроде этого в C с компилятором Microsoft С++: определенную функцию (я не помню имя), которая вызывается автоматически при каждой начальной и конечной функции.

Кроме того, вы подсчитываете количество вызовов и подколов, увеличивая и уменьшая глобальный счетчик после функции запуска и перед конечной функцией.

Например, с помощью Microsoft.NET вы можете вставить некоторый вызов функции для увеличения и уменьшения вашего глобального счетчика при каждом вызове. Он разработан JIT.

Вы также можете использовать базу данных nosql для хранения ваших вызовов.

Кроме того, есть еще одна вещь: используйте систему журналов, которая автоматически отслеживает ваши вызовы.

Кроме того, когда ваш стек вызовов заполнен, иногда он вызван рекурсивной функцией. С несколькими строками кода и объекта вы можете хранить некоторое распространение по каждой функции для каждого вызова. Это решение можно также использовать для обнаружения в любой функции особой вещи: "кто зовет меня?"

Кроме того, поскольку Java является генерируемым байтовым кодом, вы можете обнаружить байт-код вызова функции и вставить перед вызовом другой функции и после вызова другой функции, чтобы добавить свой собственный стек.