Достиг ли я пределов размера объектов, которые JavaScript в моем браузере может обрабатывать?
Я встраиваю большой массив в <script>
теги в моем HTML, как это (ничего удивительного):
<script>
var largeArray = [/* lots of stuff in here */];
</script>
в этом конкретном примере массив содержит 210 000 элементов. Это значительно ниже теоретического максимума 231 на 4 порядка величины. Вот забавная часть: если я сохраняю источник JS для массива в файл, этот файл составляет >44 мегабайт (46,573,399 байт, если быть точным).
если вы хотите увидеть сами, вы можете загрузите его из GitHub. (Все данные там консервированы, поэтому большая их часть повторяется. Это не будет иметь место в производстве.)
теперь, я действительно не волнует обслуживает столько данных. Мой сервер gzips свои ответы, так что это действительно не займет много времени, чтобы получить данные по проводу. Тем не менее, есть действительно неприятная тенденция для страницы, после загрузки, чтобы авария браузер. Я вообще не тестирую в IE (это внутренний инструмент.) Мои основные цели-Chrome 8 и Firefox 3.6.
в Firefox я вижу достаточно полезную ошибку в консоли:
Error: script stack space quota is exhausted
в Chrome я просто получаю страницу sad-tab:
переходим к делу, уже
- это действительно слишком много данных для наших современных "высокоэффективных" браузеры для обработки?
- есть что я могу сделать* корректно обрабатывать такой объем данных?
кстати, я смог заставить это работать (Читай: не разбить вкладку) ВКЛ и Выкл в Chrome. Я действительно думал, что хром, по крайней мере, был сделан из более жесткого материала, но, видимо, я ошибался...
изменить 1
@Crayon: я не искал оправдания почему я хотел бы сразу сбросить столько данных в браузер. Короткая версия: либо я решаю эту (по общему признанию не так-то просто) проблема, или я должен решить целый ряд других проблем. На данный момент я выбираю более простой подход.
@various: прямо сейчас, я не особенно ищу способы на самом деле уменьшить количество элементов в массиве. Я знаю, что могу реализовать Ajax paging или что-то еще, но это вводит свой собственный набор проблем для меня в других отношениях.
@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 гигабайт оперативной памяти, более половины терабайта дискового пространства ...и я даже не прошу браузер сделать это быстро - я просто прошу его работать на всех!☹
Edit 2
миссия выполнена!
с точечными предложениями от Хуана, а также Гуффа, я смог заставить это работать! Казалось бы, что проблема была только в анализ исходный код, не на самом деле работает с ним в памяти.
чтобы суммировать комментарий quagmire на ответ Хуана: мне пришлось разделить мой большой массив на ряд меньших, а затем Array#concat()
их, но этого было недостаточно. Я и пришлось их разделить var
заявления. Вот так:
var arr0 = [...];
var arr1 = [...];
var arr2 = [...];
/* ... */
var bigArray = arr0.concat(arr1, arr2, ...);
всем, кто внес свой вклад в решение этого: спасибо. первый раунд за мной!
*кроме очевидного: отправка меньше данных в браузер
6 ответов:
вот что я хотел бы попробовать: вы сказали, что это 44MB файл. Это, безусловно, занимает более 44 Мб памяти, я предполагаю, что это занимает гораздо больше 44 Мб оперативной памяти, может быть, половину концерта. Не могли бы вы просто сократить данные, пока браузер не рухнет, и посмотреть, сколько памяти использует браузер?
даже приложения, которые работают только на сервере, будут хорошо служить, чтобы не читать файл размером 44 Мб и хранить его в памяти. Сказав Все это, я считаю, что браузер должен быть в состоянии справиться с этим, так давайте сделаем тесты.
(с помощью Windows 7, 4 ГБ памяти)
Первый Тест Я разрезал массив пополам и не было никаких проблем, использует 80MB, нет аварии
Второй Тест Я разделил массив на два отдельных массива, но все еще содержит все данные, использует 160Mb, без сбоев
Третий Тест Поскольку Firefox сказал, что он выбежал из стека, проблема, вероятно, в том, что он не может разобрать массив сразу. Я создал два отдельных массива, arr1, arr2 затем сделал arr3 = arr1.concat (arr2); он работал нормально и использует только немного больше памяти, около 165MB.
Четвертый Тест Я создаю 7 из этих массивов (по 22 МБ каждый) и объединяю их для проверки ограничений браузера. Загрузка страницы занимает около 10 секунд. Память поднимается до 1,3 ГБ, затем она возвращается к 500 МБ. Так что да, хром может справиться с этим. Он просто не может разобрать все это сразу, потому что он использует какую-то рекурсию, как можно заметить на консоли сообщение об ошибке.
ответ создать отдельные массивы (менее 20 МБ каждый), а затем объединить их. Каждый массив должен быть на своем собственном операторе var, а не делать несколько объявлений с одним var.
Я бы все равно рассмотрел возможность извлечения только необходимой части, это может сделать браузер вялым. однако, если это внутренняя задача, это должно быть хорошо.
последний пункт: вы не на максимальных уровнях памяти, просто максимальные уровни разбора.
Да, это слишком много, чтобы просить браузера.
этот объем данных был бы управляемым, если бы это уже были данные, но это еще не данные. Учтите, что браузер должен проанализировать этот огромный блок исходного кода, проверяя, что синтаксис складывается для всего этого. После синтаксического анализа в допустимый код, код должен работать, чтобы создать фактический массив.
таким образом, все данные будут существовать (по крайней мере) в двух или трех версиях одновременно, каждая с определенным количеством накладных расходов. Как массив литерал-это один оператор, каждый шаг должен будет включать все данные.
разделение данных на несколько меньших массивов, возможно, облегчит работу браузера.
попробуйте получить данные с помощью Ajax в качестве страницы JSON. Я не знаю точный размер, но я был в состоянии вытащить большие объемы данных в Google Chrome таким образом.
вам действительно нужны все эти данные? не можете ли вы передавать только данные, необходимые в настоящее время с помощью AJAX? Как и в Google Maps - вы не можете поместить все данные карты в память браузера, они отображают только ту часть, которую вы сейчас видите.
помните, что 40 мегабайт жестких данных могут быть завышены до гораздо большего во внутреннем представлении браузера. Например, интерпретатор JS может использовать hashtable для реализации массива, что добавит дополнительные затраты памяти. Кроме того, я ожидаю, что браузеры хранят как исходный код, так и память JS, что само по себе удваивает объем данных.
JS предназначен для обеспечения взаимодействия с пользовательским интерфейсом на стороне клиента, а не для обработки нагрузок данных.
EDIT:
Кстати, вы действительно думаете, что пользователям понравится загрузка кода на 40 мегабайт? Есть еще много пользователей с менее широкополосным доступом в интернет. И выполнение скрипта будет приостановлено, пока все данные загруженный.
EDIT2:
Я посмотрел на данные. Этот массив, безусловно, будет представлен в виде хэш-таблицы. Также многие из элементов являются объектами, которые потребуют отслеживания ссылок...это дополнительная память.
Я думаю, что производительность была бы лучше, если бы это был простой вектор примитивных данных.
EDIT3: данные, безусловно, могут быть упрощены. Основная его часть-повторяющиеся строки, которые могут быть закодированы в некотором роде как целые числа или что-то. Кроме того, у моей оперы возникают проблемы с отображением текста, не говоря уже о его интерпретации.
EDIT4: забудьте объекты DateTime! Используйте метки времени или строки эпохи unix, но не объекты!
EDIT5: ваш процессор не имеет значения, потому что JS является однопоточным. И ваша оперативная память тоже не имеет значения, большинство браузеров 32bit, поэтому они не могут использовать большую часть этой памяти.
EDIT6: попробуйте изменить индексы массива последовательный целые числа (0, 1, 2, 3...). Это может сделать браузер использовать более эффективную структуру данных массива. Для эффективного доступа к элементам массива можно использовать константы. Это собирается сократить размер массива на огромный кусок.
использовать отложенную загрузку. Есть указатели на данные и получить его, когда пользователь просит.
этот метод используется в различных местах для управления миллионами записей данных.
[Edit]
Я нашел то, что искал. Виртуальная прокрутка в jqgrid. Это 500k записей, которые лениво загружаются.
Я бы попробовал иметь его как одну большую строку с разделителем между каждым "элементом", а затем использовать
split
, например:var largeString = "item1,item2,......."; var largeArray = largeString.split(",");
надеюсь, строка не будет исчерпывать стек так быстро.
Edit: для того, чтобы проверить его я создал фиктивный массив с 200 000 простых элементов (каждый элемент один номер) и Chrome загрузил его в одно мгновение. 2,000,000 товаров? Пару секунд, но без сбоев. Массив из 6 000 000 элементов (файл размером 50 МБ) сделал загрузку Chrome примерно за 10 секунд, но все же без сбоев любой путь.
Так что это заставляет меня думать, что проблема не с самим массивом, а скорее это содержание.. оптимизируйте содержимое до простых элементов, затем разберите их "на лету", и это должно работать.