Утечка памяти в JavaScript с помощью HTML5 getImageData - PullRequest
8 голосов
/ 03 октября 2011

Я работал над попыткой создать некоторые визуальные эффекты для создаваемой javascript-игры, и я заметил, что фрагмент кода, который я использую для модуляции цвета моих спрайтов, увеличивает использование памяти браузерами и выше, казалось бы, без ограничений.

Вы можете увидеть код и утечку памяти здесь: http://timzook.tk/javascript/test.html

Эта утечка памяти происходит только в моей функции updateimage (), когда я вызываю getImageData из контекста моего холста каждый кадр (через setInterval), чтобы создать новый объект ImageData для перекраски. Я бы подумал, что сборщик мусора javascript будет уничтожать старый, но если нет, то я не знаю, как уничтожить его вручную. Буду признателен за любую помощь в выяснении, почему он это делает или как это исправить.

Мой вопрос очень похож на этот: Что приводит к утечке памяти при использовании getImageData, javascript, HTML5 canvas Однако мне НУЖЕН мой код для запуска каждого кадра в функции, вызываемой setInterval, его Решение переместить его за пределы функции setInterval не вариант для меня, и я не могу оставить комментарий, спрашивающий, нашел ли он какой-то другой метод для его решения.

Примечание для людей, тестирующих его: поскольку в этом примере используется getImageData, его нельзя проверить локально, просто добавив его в файл .html, поэтому требуется веб-сервер. Также он, очевидно, использует элементы HTML5, поэтому некоторые браузеры не будут работать с ним.

Редактировать: * решено * Спасибо, решение ниже исправило это. Я не осознавал, что вы можете использовать элемент canvas точно так же, как вы можете использовать изображение в drawImage (), я реструктурировал свой код, так что теперь он использует значительно меньше памяти. Я загрузил этот измененный код на ссылку, указанную выше, если кто-то захочет его увидеть.

1 Ответ

12 голосов
/ 04 октября 2011

Вы не получаете утечку памяти при звонках на getImageData().Источником вашей проблемы является эта строка:

TempImg.src = ImgCanvas.toDataURL("image/png");

Фактически, при каждом выполнении этой строки кода браузер «загружает» другое изображение и сохраняет его в памяти.Итак, в итоге вы получите кеш, который очень быстро растет.Вы можете легко убедиться в этом, открыв сайт в Chrome и перейдя на вкладку ресурсов инструментов разработчика (ctrl+shift+i).

Чтобы обойти эту проблему, сделайте TempImgCanvas и сохраните данные изображения на этомcanvas вместо обновления объекта изображения после каждого вызова вашего цикла updateimage().

Мне нужно ненадолго отойти, но я могу привести пример через несколько часов, когда вернусь, еслиВы бы хотели.


Редактировать: Я немного изменил структуру и устранил проблему с кэшированием.Вам просто нужно сделать два изменения.Первая заменяет вашу функцию updateimage() на следующую:

function updateimage() {    
    var TempImgData = ImgContext.getImageData(0, 0, ImgCanvas.width, ImgCanvas.height);
    var NewData = TempImgData.data;
    var OrigData = ImgData.data;

    //Change image color
    var len = 4*ImgData.width*ImgData.height-1;
    for(var i=0;i<=len;i+=4) {
        NewData[i+0] = OrigData[i+0] * color.r;
        NewData[i+1] = OrigData[i+1] * color.g;
        NewData[i+2] = OrigData[i+2] * color.b;
        NewData[i+3] = OrigData[i+3];
    }

    //Put changed image onto the canvas
    ImgContext.putImageData(TempImgData, 0, 0);
}

Вторая обновляет последнюю строку в draw() следующим образом:

drawImg(ImgCanvas, Positions[i].x, Positions[i].y, Positions[i].x+Positions[i].y);

Используя этот обновленный код, мы просто ссылаемся на исходные данные базового (белого) изображения и вычисляем новые значения, основываясь на этом каждый раз, когда мы проходим через функцию updateimage().Когда вы звоните getImageData(), вы получаете копию данных изображения на холсте, поэтому, если вы редактируете холст или данные, другой остается без изменений.Для начала вы уже брали исходные данные базового изображения, поэтому имело смысл просто использовать их вместо того, чтобы заново получать их при каждом обновлении.

Кроме того, вы заметите, что я изменил вашпетля, которая немного меняет цвет изображения.Получая дескриптор фактического массива данных, к которому вы хотите обращаться / изменять, вы избавляете себя от необходимости определять местоположение массива из родительского объекта каждый раз, когда вы проходите цикл.Эта техника на самом деле приводит к довольно хорошему повышению производительности.Ваше выступление было хорошим для начала, но не помешает быть более эффективным, поскольку оно довольно прямолинейно.

...