Я думаю, що інші зробили гарну роботу, пояснивши, чому cnt> 0, але недостатньо деталей щодо того, чому cnt = 4, і чому cnt змінюється настільки сильно серед різних налаштувань. Я спробую заповнити цю порожнечу тут.
Дозволяти
- X - загальний розмір стека
- Я повинен бути простором стеку, який використовується під час першого введення основного
- R буде збільшуватися простір стеку щоразу, коли ми входимо в основний
- P - простір стека, необхідний для запуску
System.out.println
Коли ми вперше потрапимо в основний, простір, що залишився, - це XM. Кожен рекурсивний дзвінок займає більше R пам'яті. Отже, для 1 рекурсивного виклику (на 1 більше, ніж оригінальний) використання пам'яті M + R. Припустимо, що StackOverflowError кидається після успішних рекурсивних викликів C, тобто M + C * R <= X і M + C * (R + 1)> X. На час першого StackOverflowError залишилося пам'ять X - M - C * R.
Щоб мати змогу запустити System.out.prinln
, нам потрібно P кількість місця, що залишилося в стеку. Якщо так сталося, що X - M - C * R> = P, тоді буде надруковано 0. Якщо P вимагає більше місця, тоді ми видаляємо кадри зі стека, набуваючи R пам'яті ціною cnt ++.
Коли println
нарешті вдасться запустити, X - M - (C - cnt) * R> = P. Отже, якщо P великий для певної системи, то cnt буде великим.
Давайте розглянемо це на деяких прикладах.
Приклад 1: Припустимо
- X = 100
- M = 1
- R = 2
- Р = 1
Тоді C = пол ((XM) / R) = 49, а cnt = стеля ((P - (X - M - C * R)) / R) = 0.
Приклад 2: Припустимо, що
- X = 100
- M = 1
- R = 5
- Р = 12
Тоді C = 19, а cnt = 2.
Приклад 3: Припустимо, що
- X = 101
- M = 1
- R = 5
- Р = 12
Тоді C = 20, а cnt = 3.
Приклад 4: Припустимо, що
- X = 101
- M = 2
- R = 5
- Р = 12
Тоді C = 19, а cnt = 2.
Таким чином, ми бачимо, що і система (M, R, і P), і розмір стека (X) впливають на cnt.
Як бічна примітка, не важливо, скільки місця catch
потрібно для початку. Поки не буде достатньо місця для catch
, тоді cnt не збільшиться, тому немає зовнішніх ефектів.
EDIT
Я повертаю назад те, про що говорив catch
. Це відіграє певну роль. Припустимо, для запуску потрібна кількість T місця. cnt починає збільшуватись, коли простір, що залишився, перевищує T іprintln
запускається, коли простір, що залишився, перевищує T + P. Це додає додатковий крок до обчислень та додатково замулює вже мутний аналіз.
EDIT
Я нарешті знайшов час провести кілька експериментів, щоб підкріпити свою теорію. На жаль, теорія, схоже, не відповідає експериментам. Те, що насправді відбувається, дуже відрізняється.
Налаштування експерименту: сервер Ubuntu 12.04 з типовою java та jdk за замовчуванням. Xss починається з 70 000 з кроком 1 байт до 460 000.
Результати доступні за посиланням: https://www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZbcfUs3vT5MpS_OnscjWDbM
Я створив іншу версію, де видаляється кожна повторена точка даних. Іншими словами, показані лише точки, які відрізняються від попередніх. Це полегшує бачити аномалії. https://www.google.com/fusiontables/DataSource?docid=1XG_SRzrrNasepwZoNHqEAKuZlHiAm9vbEdwfsUA