- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
Steps to reproduce:
var s = "a huge, huge, huge string...";
s = s.substring(0, 5);
Expected results: s takes five bytes of memory, plus some overhead.
Actual results: s takes a huge, huge, huge amount of memory.
Unfortunately, most String functions use substring() or no-ops internally: concatenating with empty string, trim(), slice(), match(), search(), replace() with no match, split(), substr(), substring(), toString(), trim(), valueOf().
My workaround is:
function unleakString(s) { return (' ' + s).substr(1); }
But it's not satisfying, because it breaks an abstraction and forces me to think about memory allocation.
Какой time-paradox )))
1. Мне за эту питушню не заплатили ни евроцента.
2. Была и другая кодопитушня поинтереснее. Там надо было реализовать что-то новое, а не пердолиться с забытым кодом.
3. Оно и так работало. Только раз в пару лет при полном перекачивании надо было запустить 4-5 раз парсер ради 20-25к постов. Это 4-5 запусков команды вместо одного раз в пару лет. При регулярной докачке по 50-200 только изменившихся постов питушня не падает.
И ноду предлагают пихать в СЕРВЕРА (!) и ни о чём ни думать.
И тут мы опять приходим к тому о чём я уже говорил:
Они не сделали язык с автоматическим управлением памятью.
Они просто сделали говно на котором писать нельзя.
И в этом и заключается фокус.
Нода нам даёт абстракции на которых мы уже и пытаемся что-то сваять.
Очевидно, что сваять ничего нельзя - поэтому отовсюду течёт память.
Как мы видим гц-мразь опять обосралась, но с неё так никто за это и не спросил.
Вот. А я использовал серверную технологию для написания клиентского скрипта, поэтому и обосрался.
> сваять ничего нельзя - поэтому отовсюду течёт память
У меня есть сервер под Node, там крутится более нетривиальная питушня, чем парсинг базы, но даже после месяцев работы потребление остаётся в рамках 80-150МБ. Иногда смотришь: после инициализации становится 160МБ, а потом обновляешь статистику, а там ГЦ всё смыл.
Оно тоже парсит строки?
Речь о конкретной ошибке:
https://www.just-bi.nl/a-tale-of-a-javascript-memory-leak/
> использовал серверную технологию для написания клиентского скрипта, поэтому и обосрался.
Это плохо говорит о такой серверной технологии.
А самое печально что даже нет нормального метода для явного копирования части буфера в новую строку.
> return (' ' + s).substr(1);
Это хак, и довольно неочевидный.
Действительно. На C++ аналогичная программа бы заняла без GC 10 ГБ и упала бы только в 2025 году, когда постов было бы настолько много, что свободной памяти и места на диске бы не хватило.
Кстати, сейчас и 20К постов вывозит. Или 64битная версия работает с 2ГБ, или сборщик мусора стал агрессивнее.
Интересно было наблюдать за работой в content mode. Если версии с регулярками за минуту жрали память линейно, то в версии с парсером HTML за свои минуты работы GC натужно чистил память вилкой и не давал ей линейно расти. Но всё равно мой код его подебил. Причём во второй половине времени бОльшая часть ресурсов, похоже, тратилась на работу GC, чтобы урвать хоть какой-то кусок памяти.
Если бы погуглил её лет пять назад, вообще бы никаких проблем не было.
list mode: 3.2GB (во время парсинга было 3GB, потом парсинг закончился, GC убрал до 2.6GB и докачались 24 обнаруженных повреждённых файла, после сохранения JSON стало 3.2)
content mode: 5.7GB (после 4ГБ стал активнее чистить и понизил коэффициент прямой потребления, но стал безбожно тормозить, по завершении парсинга занял 5ГБ, после сохранения JSON было 5.7ГБ)
stok mode: 2.4GB
html mode: 3.7GB
Есть мысль, что content mode может заработать и на 3ГБ (с 2ГБ сломался на 22757 из 27645, а с 8ГБ просто не так агрессивно водил вилкой).
Вообще, главная проблема тут в том, что я гружу в память всю базу. Если бы это был хотя бы MySQL-сервер, Node.js бы отправила ему в запросе новую питушню и освободила целиком, отправила и освободила, и так для каждого поста на ГК.
Так что тут не надо пердолиться, если архитектура кривая.
По идее ему нужна память для обработки одной самой большой страницы (хохлосрача) + немного для статы по юзерам.
> Питушня сожрала 4.6ГБ
O(N) по памяти.
И для хранения всей базы ещё. Она же в памяти у меня, какой багор.
Мы уже поняли что вся база СТОЛЬКО не занимает.
удачи
А так я сегодня больше времени потратил на поиск и проверку, чем все эти годы - на запуск проги.
Реально работа с gc языком включает в себя снятие дампов, изучение таких флагов, тюнинг gc.
Программист: Реально работа с gc языком включает в себя снятие дампов, изучение таких флагов, тюнинг gc.
Подмодули парсеров разных режимов в качестве интерфейса получают функции добавления поста, комментария и пользователя. Соответственно, если в них провести очистку строки от греха, эффект сработает сразу для всех парсером.
Добавил такое:
и обернул в это пришедшую от парсеров питушню
После этого в режиме pitux=4096 график mode content стал пилообразным. Намечался линейный рост но раз секунд в 10 ГЦ сбрасывал по 200-300МБ.
Парсинг закончился на 1.5ГБ, сохранение - на 2.1ГБ.
В режиме pitux=1024 (ко-кок, даже меньше, чем было с дефолтом) поцребление было с микропилами (ГЦ срезал десятками мегабайт), линейного роста не было. Что интересно, после опреелённого момента приложение тормозит секунд 5 и тактика ГЦ меняется. Для 4096 это были просто тормоза и линейный рост продолжался, а для 1024 стало линейное убывание. Парсинг закончился на 880МБ, сохранение - на 1.6ГБ.
Мда, мы с Вами были правы про
> кусков строк, от которых используется только небольшой слайс
Парсер стока, судя по размеру, реально тянул за собой все считанные из файлов данные.
Вот было бы у вас например 512 Мб памяти, тогда программа сразу бы получилась гораздо оптимальнее.
Чем больше современному программисту доступно памяти, тем большее говнище он пишет.
P.S. Уверен, что скрипт бы и без очистки строк работал и так месяцами, поскольку при обновлении поста GC бы удалял старую психозу :) Правда, на 4-8ГБ
Не было бы JS и куч памяти - не было бы всей питушни, что я писал. На сишке под микроконтроллер я бы не брался писать парсер ГК.
Выскоуровневые ЯПы быстро развращают просто. Если бы ты никогда в жизни не ел ничего слаще `Borland C 3.1` под DOS, то как миленький бы писал на нём что угодно.
Писали же чуваки всякие тосеры, редакторы для фидонета, целые файловые менеджеры...
Ой не факт... Сейчас везде тенденция сводить полезную функциональность к минимуму.
или c собственным gc:
можно даже фоновым:
Сравнение немного некорректно.
Во-первых, js не предоставляет программисту альтернативной реализации строк.
Во-вторых, они даже не удосужились добавить метод подобный «squeeze».
Программист вынужден использовать неочевидные, недокументированные хаки, основанные на предположениях как оно работает внутри.
Проблема эта существует много лет. Но решать её не торопятся. Вместо этого в новые стандарты напихивают разные class, constructor, extends, super,
И самое главное: В-третьих, достойная реализация гц в случае критической нехватки памяти могла бы анализировать ссылки подстрок (которые полностью спрятаны под капотом) и копировать использующиеся фрагменты в меньшие подстроки.
Ничего этого в js нет.
Это было бы самое лучшее решение. И не было бы тормозов из-за лишних маллоков как в разных крестах, и память бы не просиралась.
Оптимизация с хранением слайсов, в общем-то, полезная, пока памяти на неё хватает.
1024-- рассказал что в node утекает память.
https://govnokod.ru/27615#comment664411
> Мой парсер постов ГК (работает с HTML-файлами постов на диске, которые скачал загрузчик) раньше мог распарсить не более 5К постов, от большего количества он тупо набирал 1.5ГБ и дох.
> Сборщику мусора просто не давали нормально отработать, какие тут тормоза!
> Вероятно, память жрётся из-за ошмётков объектов парсинга и кусков строк, от которых используется только небольшой слайс.