Достиг ли я пределов размера объектов, которые может обрабатывать JavaScript в моем браузере?
Я встраиваю большой массив в <script>
теги в моем HTML, вот так (ничего удивительного):
<script>
var largeArray = [/* lots of stuff in here */];
</script>
В этом конкретном примере массив содержит 210000 элементов. Это значительно ниже теоретического максимума в 2 31 - на 4 порядка. Вот забавная часть: если я сохраню исходный код JS для массива в файл, этот файл будет иметь размер>44 мегабайт (точнее, 46 573 399 байт).
Если вы хотите убедиться в этом сами, вы можете скачать его с GitHub. (Все данные там являются консервированными, поэтому большая их часть повторяется. Это не будет иметь место в производстве.)
Теперь я действительно не беспокоюсь об обслуживании такого количества данных. Мой сервер записывает свои ответы, поэтому на получение данных по проводам уходит совсем немного времени. Тем не менее, существует очень неприятная тенденция к загрузке страницы после сбоя браузера. Я вообще не тестирую в IE (это внутренний инструмент). Мои основные цели - Chrome 8 и Firefox 3.6.
В Firefox я вижу достаточно полезную ошибку в консоли:
Error: script stack space quota is exhausted
В Chrome я просто получаю страницу с грустной вкладкой:
Погоня уже
- Не слишком ли много данных для наших современных "высокопроизводительных" браузеров?
- Могу ли я что-нибудь сделать, чтобы изящно обрабатывать такое количество данных?
Между прочим, я смог заставить это работать (читай: не сбивать вкладку) время от времени в Chrome. Я действительно думал, что Chrome, по крайней мере, сделан из более сложных вещей, но, видимо, я ошибался...
Редактировать 1
@Crayon: я не пытался оправдать, почему я хотел бы выложить столько данных в браузер сразу. Короткая версия: либо я решаю эту (по общему признанию, непростую) проблему, либо мне приходится решать целый ряд других проблем. Сейчас я выбираю более простой подход.
@various: сейчас я не особо ищу способы реально уменьшить количество элементов в массиве. Я знаю, что мог бы реализовать Ajax-пейджинг или что-то еще, но это создает для меня ряд других проблем.
@Phrogz: каждый элемент выглядит примерно так:
{dateTime:new Date(1296176400000),
terminalId:'terminal999',
'General___BuildVersion':'10.05a_V110119_Beta',
'SSM___ExtId':26680,
'MD_CDMA_NETLOADER_NO_BCAST___Valid':'false',
'MD_CDMA_NETLOADER_NO_BCAST___PngAttempt':0}
@Will: но у меня есть компьютер с 4-ядерным процессором, 6 гигабайтами оперативной памяти, более половины терабайта дискового пространства... и я даже не прошу браузер сделать это быстро - я просто прошу чтобы это работало вообще! ☹
Редактировать 2
Миссия выполнена!
Благодаря точным предложениям от Хуана и Гуффы, я смог заставить это работать! Может показаться, что проблема была только в разборе исходного кода, а не в работе с ним в памяти.
Подводя итог болоту комментария к ответу Хуана: мне пришлось разбить мой большой массив на ряд меньших, а затем Array#concat()
их, но этого было недостаточно. Я также должен был поместить их в отдельный var
заявления. Как это:
var arr0 = [...];
var arr1 = [...];
var arr2 = [...];
/* ... */
var bigArray = arr0.concat(arr1, arr2, ...);
Всем, кто внес свой вклад в решение этой проблемы: спасибо. Первый раунд на мне!
* кроме очевидного: отправка меньшего количества данных в браузер
6 ответов
Вот что я бы попробовал: вы сказали, что это файл размером 44 МБ. Это, безусловно, занимает более 44 МБ памяти, я предполагаю, что это занимает более 44 МБ ОЗУ, может быть, половина гигабайта. Не могли бы вы просто урезать данные, пока браузер не потерпит крах и посмотреть, сколько памяти использует браузер?
Даже приложения, которые работают только на сервере, будут хорошо работать, если они не будут читать файл размером 44 МБ и хранить его в памяти. Сказав все это, я считаю, что браузер должен быть в состоянии справиться с этим, поэтому позвольте мне провести несколько тестов.
(С использованием Windows 7, 4 ГБ памяти)
Первый тест Я разрезал массив пополам и не было никаких проблем, использует 80 МБ, без сбоев
Второй тест Я разбил массив на два отдельных массива, но по-прежнему содержит все данные, использует 160 МБ, без сбоев
Третий тест Поскольку Firefox сказал, что он исчерпал стек, проблема, вероятно, в том, что он не может проанализировать массив сразу. Я создал два отдельных массива, arr1, arr2, затем сделал arr3 = arr1.concat(arr2); Он работал нормально и использует только немного больше памяти, около 165 МБ.
Четвертый тест Я создаю 7 из этих массивов (по 22 МБ каждый) и объединяю их для проверки ограничений браузера. Загрузка страницы занимает около 10 секунд. Объем памяти увеличивается до 1,3 ГБ, а затем уменьшается до 500 МБ. Так что да, хром может справиться с этим. Он просто не может проанализировать все сразу, потому что использует какую-то рекурсию, что можно заметить в сообщении об ошибке консоли.
Ответ Создайте отдельные массивы (размером менее 20 МБ каждый) и затем объедините их. Каждый массив должен быть в своем собственном операторе var, вместо того, чтобы делать несколько объявлений с одним var.
Я бы по-прежнему рассматривал возможность получения только необходимой части, это может сделать браузер вялым. однако, если это внутренняя задача, это должно быть хорошо.
Последний пункт: вы не на максимальных уровнях памяти, просто максимальные уровни синтаксического анализа.
Да, это слишком много, чтобы спросить у браузера.
Этот объем данных будет управляемым, если это уже были данные, но это еще не данные. Учтите, что браузер должен анализировать этот огромный блок исходного кода, проверяя, что синтаксис складывается для всего этого. После анализа в действительный код, код должен быть запущен для создания фактического массива.
Таким образом, все данные будут существовать в (по крайней мере) двух или трех версиях одновременно, каждая с определенным объемом служебной информации. Поскольку литерал массива представляет собой один оператор, каждый шаг должен включать все данные.
Разделение данных на несколько меньших массивов может облегчить работу браузера.
Вам действительно нужны все данные? Вы не можете передавать только те данные, которые необходимы в данный момент, используя AJAX? Подобно Google Maps - вы не можете поместить все данные карты в память браузера, они отображают только ту часть, которую вы сейчас видите.
Помните, что 40 мегабайт жестких данных может быть увеличено до гораздо большего во внутреннем представлении браузера. Например, интерпретатор JS может использовать хеш-таблицу для реализации массива, что добавит дополнительные накладные расходы памяти. Кроме того, я ожидаю, что браузеры хранят как исходный код, так и память JS, что само по себе удваивает объем данных.
JS предназначен для обеспечения взаимодействия пользовательского интерфейса на стороне клиента, а не для обработки данных.
РЕДАКТИРОВАТЬ:
Кстати, вы действительно думаете, что пользователям понравится скачивать код на 40 мегабайт? Все еще есть много пользователей с менее широкополосным доступом в Интернет. И выполнение скрипта будет приостановлено, пока все данные не будут загружены.
EDIT2:
Я посмотрел на данные. Этот массив определенно будет представлен как хеш-таблица. Также многие элементы являются объектами, которые требуют отслеживания ссылок... это дополнительная память.
Я думаю, производительность была бы лучше, если бы это был простой вектор примитивных данных.
РЕДАКТИРОВАТЬ 3: Данные, безусловно, могут быть упрощены. Большая часть этого - повторяющиеся строки, которые могут быть закодированы как целые числа или как-то еще. Кроме того, в моей Opera возникают проблемы с отображением текста, не говоря уже о его интерпретации.
РЕДАКТИРОВАТЬ 4: Забудьте объекты DateTime! Используйте временные метки или строки эпохи Unix, но не объекты!
EDIT5: ваш процессор не имеет значения, потому что JS является однопоточным. И ваша оперативная память также не имеет значения, большинство браузеров 32-битные, поэтому они не могут использовать большую часть этой памяти.
EDIT6: Попробуйте изменить индексы массива на последовательные целые числа (0, 1, 2, 3...). Это может заставить браузер использовать более эффективную структуру данных массива. Вы можете использовать константы для эффективного доступа к элементам массива. Это сократит размер массива на огромный кусок.
Попробуйте получить данные с помощью Ajax в виде страницы JSON. Я не знаю точного размера, но я смог перенести большие объемы данных в Google Chrome таким образом.
Используйте ленивую загрузку. Имейте указатели на данные и получите их, когда пользователь спрашивает.
Этот метод используется в разных местах для управления миллионами записей данных.
[Редактировать]
Я нашел то, что искал. Виртуальная прокрутка в jqgrid. Это 500 тыс. Записей, загружаемых лениво.
Я хотел бы попытаться иметь его как одну большую строку с разделителем между каждым "элементом", а затем использовать split
, что-то вроде:
var largeString = "item1,item2,.......";
var largeArray = largeString.split(",");
Надеюсь, строка не исчерпает стека так быстро.
Изменить: чтобы проверить это, я создал фиктивный массив с 200 000 простых элементов (каждый элемент по одному номеру), и Chrome загрузил его в одно мгновение. 2 000 000 предметов? Пару секунд но без сбоев. Массив из 6 000 000 элементов (файл размером 50 МБ) загружал Chrome в течение примерно 10 секунд, но все равно сбой не происходил ни в одном из способов.
Так что это наводит меня на мысль, что проблема не в самом массиве, а в его содержимом. Оптимизируйте содержимое до простых элементов, затем анализируйте их "на лету", и это должно работать.