java.lang.StackOverflowError при выполнении глубокой рекурсии с Java 1.6 и ОС Windows 7

У меня есть программа, которая во время выполнения столкнется с очень глубокой рекурсией. В середине этого я получаю java.lang.StackOverflowError, и мое приложение зависает. Я использую JDK 1.6 и ОС Windows 7.

Странно то, что я не получаю этот StackOverflowError с той же кодовой базой при запуске моего приложения с Java 1.5 и Windows XP. Я понял, что размер стека по умолчанию будет отличаться от платформы к платформе и от ОС к ОС. Но с Java 1.5 и Windows XP я установил размер стека 256 КБ с помощью команды -Xss и не получил эту ошибку. С теми же 256 КБ в ОС Windows 7 и Java 1.6 я получаю эту ошибку.

С размером стека 256 КБ мое приложение работает без проблем в Windows XP и выдает StackOverflowError в ОС Windows 7.

Поэтому, пожалуйста, предоставьте любую информацию о том, как размеры стека потоков будут отличаться от ОС Windows 7 до Windows XP??


person user3244279    schedule 28.01.2014    source источник
comment
можешь поделиться своим кодом?   -  person Mureinik    schedule 28.01.2014
comment
Привет. Это не дубликат, мой друг. Мой вопрос в том, что мое приложение работает без каких-либо проблем в WInodws Xp с размером стека 256 КБ. Но в Windows 7 это дает мне ошибку переполнения стека с тем же размером стека 256 КБ. Увеличение размера стека в Windows 7 с помощью -Xss решит проблему. Но странно, почему он не работает в Windows 7 с таким же размером стека?   -  person user3244279    schedule 28.01.2014
comment
Кто-нибудь знает, в каких единицах 256к? Байты или кадры? Это может иметь значение, потому что тогда кадры в 64-битной системе могут быть больше из-за большего размера указателя. Следовательно, в 256 КБ помещается меньше кадров, если единицей измерения является байт.   -  person tilpner    schedule 28.01.2014


Ответы (1)


Я думаю, что ответ в том, что JDK 1.5 игнорирует установку размера стека по умолчанию на определенное значение. Скажем, если у вас размер стека по умолчанию 512 КБ и (вы верите в это) вы установили его на 256 КБ, в конечном итоге он все равно будет иметь значение 512 КБ. В JDK 6 вы также устанавливаете размер по умолчанию 256 КБ, но здесь он действительно будет иметь это значение. Это может быть причиной того, что ваша Windows 7 получает ошибку (ранее)!


Посмотрите на эту таблицу -Xss по умолчанию:

Platform         Default
----------------------------------
Windows IA32     64 KB
Linux IA32       128 KB
Windows x86_64   128 KB
Linux x86_64     256 KB
Windows IA64     320 KB
Linux IA64       1024 KB (1 MB)
Solaris Sparc    512 KB 

Здесь вы можете видеть, что значения по умолчанию различаются в зависимости от платформы, на которой вы работаете.

См.: http://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html#wp1024112

Кроме того, поскольку вы получаете StackOverflowError в одной системе, но не в другой, 32-разрядная виртуальная машина будет хранить адреса в 4 байтах памяти. 64-битная виртуальная машина будет хранить то же самое с 8 байтами! Итак, если вы установите значение размера стека в 256 КБ (да, это килобайты), 32-разрядная система (вероятно, ваша система XP) может хранить 65 536 адресов в своей памяти стека. 64-битная система (ваша система Windows 7) может хранить только 32 768 адресов. Это может быть проблемой между этими двумя системами.

Как правило, Java объекты потребляют тот же объем памяти в 32-разрядной виртуальной машине, что и в 64-разрядной ВМ. Очевидно, что если у вас есть Object, который ссылается на 100 других Object, это увеличит размер основного Object на 400 байт на 64-битной JVM.

Дополнительно сказано:

Обратите внимание, что в некоторых версиях Windows ОС может округлять размеры стека потоков, используя очень грубую гранулярность. Если запрошенный размер меньше размера по умолчанию на 1 КБ или более, размер стека округляется до значения по умолчанию; в противном случае размер стека округляется до числа, кратного 1 МБ.

64 КБ — это наименьшее количество пространства стека, разрешенное для каждого потока.

См.: http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#threads_oom

К сожалению, я не нашел конкретного объяснения, как выражается грубая детализация.

Попробуйте запустить следующий пример программы на обеих платформах, чтобы угадать, насколько размеры стеков различаются между собой:

public class StackOverflowTest {
    public static void main(String[] args) {
        recurse(0);
    }

    private static void recurse(int i) {
        try {
            recurse(++i);
        } catch (StackOverflowError e) {
            System.out.println(i);
            System.exit(0);
        }
    }
}

Результаты на моей машине:

Windows 7 (64-bit)

Stack calls     Java           Stack size
------------------------------------------
11424           JDK 5 64-bit   default
11424           JDK 5 64-bit   -Xss256K
11424           JDK 5 64-bit   -Xss1024K
6260            JRE 6 32-bit   default
4894            JRE 6 32-bit   -Xss256K
35405           JRE 6 32-bit   -Xss1024K
10448 to 10468  JDK 7 64-bit   default
2255 to 2274    JDK 7 64-bit   -Xss256K
10448 to 10468  JDK 7 64-bit   -Xss1024K
10396 to 41894  JDK 8 64-bit   default
2203 to 4590    JDK 8 64-bit   -Xss256K
10396 to 41894  JDK 8 64-bit   -Xss1024K

(Было бы здорово, если бы список можно было расширить за счет других систем, на которых вы работаете!)

Как видите, размеры стеков — полная загадка. Это может быть связано с тем, что разные версии Java занимают разное место в памяти стека.

Кроме того, если вы установите размер стека с помощью -Xss, значение будет игнорироваться в выпуске Java 5.

С JDK 7 вы не всегда получаете тот же результат, что и в других выпусках. То же самое касается JDK 8, где диапазоны существенно различаются.

Возможно, вы сами разберетесь, отличается ли он лишь незначительно или нет на ваших собственных системах.

Дополнительная литература: http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.5.2

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

Итак, вопрос в том, что подразумевается под динамически расширяться?

Затем я обнаружил ошибку в исходниках, которая может представлять интерес, если вы используете более раннюю версию Java 6:

http://bugs.java.com/view_bug.do?bug_id=6316197

person Community    schedule 28.01.2014
comment
Привет. Спасибо за ваш ответ. Теперь ясно, что размер стека по умолчанию будет отличаться от платформы к платформе. Но если мы установим одинаковый размер стека в Windows XP и Windows 7, приложение успешно работает в Windows XP и не работает в Windows 7. есть ли какая-то причина. Очень ценю ваш ответ по этому поводу. Заранее спасибо. - person user3244279; 28.01.2014
comment
Я предполагаю, что ваша система Windows 7 является 64-битной версией, а ваша XP - 32-битной. Так что это может повлиять на то, как используется память! - person bobbel; 28.01.2014
comment
Я расширил свой ответ, чтобы различать 32- и 64-битные системы. - person bobbel; 28.01.2014
comment
Привет. Ваше объяснение разницы между 32-битной и 64-битной версиями мне понятно и может быть правдой. Но я использую 32-разрядную версию как для Windows XP, так и для Windows 7. В этом случае потребление памяти будет иметь значение? - person user3244279; 29.01.2014
comment
Я изменил свой ответ и добавил пример программы, которую вы можете попробовать выполнить на обеих платформах. Может быть, вы что-нибудь придумаете! Ваш вопрос на самом деле очень хороший ;) - person bobbel; 29.01.2014
comment
Эй, спасибо за вашу помощь. Ваша информация и предоставленная ссылка действительно помогли мне понять проблему. Еще раз спасибо за помощь. Я понял, что размер стека по умолчанию для java 6 в winodws составляет 320 КБ. У вас есть идеи, каков размер стека по умолчанию для Java 5? В java 5 jvm не учитывает значение paasing с помощью -Xss. Он всегда выполняет 12 472 рекурсии независимо от размера, установленного u из -Xss. - person user3244279; 30.01.2014
comment
Я добавил некоторые результаты тестов для разных версий Java (а также предоставил лучшую тестовую программу). Это очень любопытно... Было бы неплохо, если бы вы могли предоставить свои результаты на вашей системе XP и Win7 с 32-битной версией! - person bobbel; 30.01.2014
comment
@bobbel: In general, Java objects consuming the same size of memory in a 32 as in a 64-bit VM. Вы уверены? В конечном итоге на объекты ссылаются через указатель памяти. Ссылки на объекты будут больше, как и пространство, необходимое для ссылки на любые объекты, которые исходный объект определяет как свойства или поля. - person Eric J.; 17.07.2015
comment
Возможно, вы правы в том, что ссылки в объекте Java увеличивают потребление памяти самим объектом. Но сами по себе данные его не увеличат. Очевидно, что если у вас есть Object, который ссылается на 100 других Object, это увеличит размер основного Object на 400 байт на 64-битной JVM. Итак, хороший момент здесь от вас! - person bobbel; 17.07.2015