Подтвердить что ты не робот

Стек против кучи в Javascript? (Максимальный размер стека вызовов)

Я пытаюсь создать веб-страницу, для которой мне нужно перекопать около 100 МБ данных в JavaScript. В разных браузерах я сталкиваюсь с ошибками "максимальный размер стека вызовов" при разных объемах данных.

Могу ли я исправить эту проблему, пройдя мой код и пытаясь переместить локальные переменные внутри функций в более глобальную область, чтобы попытаться их распределить по куче вместо стека? Или эти понятия не существуют в JavaScript? (Насколько я знаю, у меня нет никаких рекурсивных циклов в моих данных, поэтому на самом деле это пара огромных строк/массивов чисел, которые, по-видимому, вызывают ошибку)

Если это невозможно, есть ли способы попросить браузер зарезервировать больше памяти?

4b9b3361

Ответ 1

В Javascript нет разделения памяти на стек/кучу. То, что вы видите, может быть одним из следующих:

  • Рекурсия, которая проходила слишком глубоко. В этом случае вам нужно будет пересмотреть свой алгоритм, чтобы сделать его более итеративным и использовать меньше рекурсии, чтобы вы не ударили ограничения стек вызовов, наложенные браузерами.
  • Если ваш алгоритм не имеет глубокой рекурсии, это может быть всего лишь достаточно глубокий вызов, учитывая, что ваш код сгенерирован.
  • Наконец, некоторые механизмы могут выделять аргументы функции и обладать указанными переменными на некотором внутреннем стеке для быстрого поиска. Если вы (или автоматически сгенерированный код), в буквальном смысле, используете тысячи локальных переменных или аргументов в функции, это может также превысить ограничения для конкретного двигателя.

Ответ 2

ОК, выяснил проблему. В моем коде действительно не было рекурсии. Действительно, можно вызвать функции JavaScript с сотнями аргументов, если они являются функциями "varargs", например, <array>.splice(...), что было моим нарушителем.

Кроме того: GWT реализует функцию Java System.arraycopy(...) с использованием функции сплайсинга JavaScript более или менее умным способом.

splice принимает произвольное количество входных элементов для вставки в целевой массив. Эти входные элементы можно передать из другого массива, используя следующую конструкцию:

var arguments = [index, howmany].concat(elements);
Arrays.prototype.splice.apply(targetarray, arguments);

Это эквивалентно вызову:

targetarray.splice(index, howmany, elements[0], elements[1], elements[2], ...);

Если элементы становятся большими (см. ниже, что означает "большой" для разных браузеров), вы можете получить ошибку "Максимальный размер стека вызовов" без рекурсии как содержимое его будет загружено в стек для вызова функции.

Здесь короткая script, которая демонстрирует эту проблему:

var elements = new Array();
for (i=0; i<126000; i++) elements[i] = 1;
try {
    var arguments = [0, 0].concat(elements);
    Array.prototype.splice.apply(elements, arguments);
    alert("OK");
} catch (err) {
    alert(err.message);
}

Используя этот script, "большой" означает следующее:

  • Chrome 19: элементы содержат ~ 125 000 номеров
  • Safari 5.1 (в Windows): элементы содержат ~ 65 000 номеров
  • Firefox 12: элементы содержат ~ 500 000 номеров
  • Opera 11.61: элементы содержат ~ 1,000,000 номеров

И победитель: Internet Explorer 8 для изменения! Он может использовать всю системную память, прежде чем этот вызов функции завершится неудачно.

Замечание: Firefox и Opera фактически генерируют другое (более полезное) сообщение об ошибке: Function.prototype.apply: argArray слишком велико