Подтвердить что ты не робот

Утечка URI данных в Safari (была: Утечка памяти с холстом HTML5)

Я создал веб-страницу, которая получает растровые изображения с кодировкой base64 через Websocket и затем рисует их на холсте. Он работает отлично. Кроме того, использование обозревателя (будь то Firefox, Chrome или Safari) увеличивается с каждым изображением и никогда не опускается. Таким образом, в моем коде должна быть утечка памяти или какая-либо другая ошибка. Если я прокомментирую вызов context.drawImage, утечка памяти не произойдет (но тогда, конечно, изображение никогда не рисуется). Ниже приведены фрагменты с моей веб-страницы. Любая помощь приветствуется. Благодарю!

// global variables
var canvas;
var context;

...

ws.onmessage = function(evt)
{
    var received_msg = evt.data;
    var display_image = new Image();
    display_image.onload = function ()
    {
        context.drawImage(this, 0, 0);
    }
    display_image.src = 'data:image/bmp;base64,'+received_msg;
}

...

canvas=document.getElementById('ImageCanvas');
context=canvas.getContext('2d');

...

<canvas id="ImageCanvas" width="430" height="330"></canvas>

ОБНОВЛЕНИЕ 12/19/2011

Я могу обойти эту проблему, динамически создавая/уничтожая холст каждые 100 изображений с помощью createElement/appendChild и removeChild. После этого у меня больше нет проблем с памятью в Firefox и Chrome.

Однако Safari по-прежнему имеет проблему с использованием памяти, но я думаю, что это другая проблема, не связанная с Canvas. Кажется, что существует проблема с многократным изменением "src" изображения в Safari, как будто он никогда не освободит эту память.

display_image.src = 'data:image/bmp;base64,'+received_msg;  

Это та же проблема, описанная на следующем сайте: http://waldheinz.de/2010/06/webkit-leaks-data-uris/


ОБНОВЛЕНИЕ 12/21/2011

Я надеялся обойти эту проблему Safari, переведя полученную строку base64 в blob (с функцией "dataURItoBlob", которую я нашел на этом сайте) и обратно к URL-адресу с окном .URL.createObjectURL, установив мое изображение src к этому URL-адресу, а затем освободить память, вызвав window.URL.revokeObjectURL. Я все это работаю, и Chrome и Firefox отображают изображения правильно. К сожалению, Safari, похоже, не поддерживает BlobBuilder, поэтому это не решение, которое я могу использовать. Это странно, поскольку во многих местах, включая книгу "Программирование приложений HTML5 O'Reilly", указано, что BlobBuilder поддерживается в Safari/WebKit Nightly Builds. Я загрузил последнюю новинку Windows из http://nightly.webkit.org/ и запустил WebKit.exe, но BlobBuilder и WebKitBlobBuilder все еще undefined.


ОБНОВЛЕНИЕ 01/03/2012

Хорошо, я, наконец, исправил это, расшифровав строку URI данных с кодировкой base64 с помощью atob(), а затем создав массив данных пикселей и записав его на холст с помощью putImageData (см. http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/). Выполняя это так (в отличие от постоянной модификации изображения "src" и вызова drawImage в функции onload), я больше не вижу утечки памяти в Safari или в любом браузере.

4b9b3361

Ответ 1

Без фактического рабочего кода мы можем только предположить, почему.

Если вы отправляете один и тот же образ снова и снова, каждый раз создаете новое изображение. Это плохо. Вы хотите сделать что-то вроде этого:

var images = {}; // a map of all the images

ws.onmessage = function(evt)
{
    var received_msg = evt.data;
    var display_image;
    var src = 'data:image/bmp;base64,'+received_msg;
    // We've got two distinct scenarios here for images coming over the line:
    if (images[src] !== undefined) {
      // Image has come over before and therefore already been created,
      // so don't make a new one!
      display_image = images[src];
      display_image.onload = function () {
          context.drawImage(this, 0, 0);
      }
    } else {
      // Never before seen image, make a new Image()
      display_image = new Image();
      display_image.onload = function () {
          context.drawImage(this, 0, 0);
      }
      display_image.src = src;
      images[src] = display_image; // save it for reuse
    }
}

Есть более эффективные способы написать это (я дублирую код onload, например, и я не проверяю, завершено ли изображение). Я оставлю эти части до вас, но вы поняли.

Ответ 2

Я не считаю, что это ошибка. Проблема заключается в том, что изображения сложены друг на друга. Чтобы очистить память, вам нужно использовать clearRect(), чтобы очистить холст перед тем, как нарисовать в нем новое изображение.

ctx.clearRect(0, 0, canvas.width, canvas.height);

Как очистить содержимое холста

Ответ 3

вы, вероятно, рисуете изображение намного больше, чем ожидаете. попробуйте добавить счетчик и вывести номер в оповещение или на div на странице, чтобы увидеть, сколько раз изображение рисуется.

Ответ 4

Это очень интересно. Это стоит сообщить как ошибку для разных поставщиков браузеров (я чувствую, что этого не должно быть). Вы могли бы откликаться по строкам "Не делай этого, вместо этого делай то-то и то-то", но, по крайней мере, тогда ты узнаешь правильный ответ и напишешь интересную статью для записи в блоге (больше людей обязательно столкнутся с этот вопрос).

Одна вещь, которую нужно попробовать, - отключить изображение src (и обработчик onload) сразу после вызова drawImage. Это может не освободить всю память, но она может получить большую часть ее.

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

Сообщите свои результаты. Мне очень интересно, потому что я использую подобную технику для одной из кодировки tightPNG в noVNC (и я уверен, что другие будут заинтересованы тоже).