Web Worker потребляет огромное количество памяти

Я пытаюсь улучшить производительность в своем приложении, которое сравнивает содержимое двух больших холстов.

Проблема заключается в том, что во время сравнения основной поток браузера блокируется, что приводит к невосприимчивому пользовательскому интерфейсу. Время выполнения функции сравнения занимает около 10 мс, а сравнение происходит каждые 250 мс или 500 мс.

Чтобы решить эту проблему, я придумал использовать другой поток, создав web worker. Сейчас он работает довольно хорошо, но я понял, что он потребляет безумный объем памяти (до 600 МБ в FF и Chrome - в Edge до 70 МБ, он никогда не достигал 100 МБ в однопоточной версии во всех браузерах).

Я думал, что оставил некоторые ссылки, и Garbage Collector не может освободить память. Однако через некоторое время (целый день снятия моего приложения с фрагментов и попыток delete, null или undefined переменных/данных) я создал скрипку ниже, в которой я отправляю 1MB ArrayBuffer (хотя Я передал его, передав), но без обработки, и, как вы видите, он также потребляет огромное количество памяти.

Любые идеи, как я могу решить эту проблему (любые альтернативные решения или любые возможные улучшения работника) и где проблема?

var sortFilterDataWorker = function () {
    onmessage = function image2compare(ev) {
      postMessage('hi');
    };
}.toString();

/* PREPARE WORKER AS STRING TO CREATE JS BLOB FILE */
sortFilterDataWorker = sortFilterDataWorker.slice(sortFilterDataWorker.indexOf('{') + 1, -1).trim();

var blob = new Blob([sortFilterDataWorker]) // create blob file with worker code
  , blobUrl = window.URL.createObjectURL(blob) // create pseudo url to blob file
  , compareWorker = new Worker(blobUrl) 
  ;

setInterval(function(){
  var oneMB = new ArrayBuffer(8388608);
  compareWorker.postMessage(oneMB, [oneMB]); // transpile ArrayBuffer
}, 250);

FIDDLE

EDIT:

Я узнал, что если я прекращаю работу, то каждый n повторяет и создает новую, быстрее выпускает память. Но это еще не решение, просто курио.

Ответы

Ответ 1

Я обнаружил, что если я запускаю сборщик мусора вручную из инструментов разработчика → Timeline, он очищает всю память. Точно так же, если я начну взаимодействовать с контекстом Worker с консоли, вызывающие функции, похоже, случайным образом запускают успешный gc.

Основываясь на этом, я бы сказал, что нет ссылки на зависание, но получение объектов через передачу может не привести к проверке gc в качестве новых запросов на распределение.

Перенос объекта обратно с ответом, похоже, обходит проблему:

postMessage('hi', [ev.data]);  // process usage stays around 50MB

В качестве альтернативы, убедитесь, что Рабочий является нетривиальным и ему нужно будет делать нормальные распределения, также, похоже, правильно запускает gc, i.e:

  postMessage('hi');
  var twoMB = new ArrayBuffer(8388608); // usage cycles 70MB - ~220MB

Ответ 2

Не забудьте прекратить работника после использования, вызвав: worker.terminate()

Это вызвало правильное GC по крайней мере в Safari, который также имел проблемы с памятью.