У меня есть веб-страница, которая использует jquery для получения некоторой информации о продукте, когда люди смотрят на вещи, а затем отображает последние изображения продуктов, которые были просмотрены. Это в обратном вызове jquery AJAX, который выглядит примерно так:
if (number_of_things_seen > 10) {
$('#shots li:last-child').remove();
}
$('<li><img src = "' + p.ProductImageSmall + '"></li>').prependTo('#shots');
Однако, похоже, происходит утечка довольно небольшого количества памяти. Визуально все работает правильно, но след бесконечно растет.
Инспектор DOM Safari показывает, что DOM такая, как я ожидал, но, похоже, поддерживает ссылки на каждое отображаемое изображение (как показано в этот скриншот на случай, если кому-то интересно).
Я добавил
$('#shots li:last-child img').remove();
К заявлению об удалении без заметного эффекта.
Нужна ли какая-то магия, чтобы позволить браузеру выпустить некоторые из этих вещей?
Нет. Он все еще течет как сумасшедший. Я сделал другую версию приложения, которая имеет существенно другой механизм отображения, но я все еще вижу утечку изображений.
Когда вы пишете тестовый пример, скажем, один тег img и цикл JavaScript, который заменяет его атрибут src из кучи URL-адресов (без JS-фреймворка, только большой статический массив URL-адресов и трехстрочный цикл) - он все еще протекает ? Если так, то вам не повезло, если не все исправить.
Немного больше работы, но обслуживайте ваши изображения через php-скрипт без заголовка кеша, вместо того, чтобы вызывать их напрямую.
Можете ли вы попробовать изменить src последнего ребенка и посмотреть, имеет ли это значение? Затем вы можете переместить этот элемент, чтобы он стал первым дочерним элементом
//not tested
var $list=$('#shots>li');
$list.filter(':last-child').children('img')
.attr('src', p.ProductImageSmall)
.parent()
.insertBefore( $list.eq(0) );
Мне нравится код, к которому привело это предложение, но изображения все еще не исчезают.
Дастин, когда ты говоришь, что изображения не исчезают, ты можешь уточнить? Вы имеете в виду, что он не меняет местами изображения?
Это странно, так как удаление в jquery оптимизировано для предотвращения утечек. может быть, это создание утечек xhr-объектов?
До того, как я стал показывать изображения, они просачивались гораздо медленнее - что-то порядка миллиона обновлений, прежде чем я увидел, что оно превышает 100 МБ.
Дастин, можешь уточнить, не работала ли смена src изображения?
Да, я сделал почти в точности тот код. Я инициализировал список из 10 элементов с прозрачным изображением размером 1 пиксель и циклически прокрутил их таким образом (установив src и title для каждого из них). Увидел ту же проблему роста изображения.
Как долго вы наблюдаете, как это «бесконечно растет»? некоторые реализации сборщиков мусора не обязательно возвращают память ОС так быстро, если вообще возвращают. Можете ли вы превратить то, что вы пытаетесь сделать, в настоящий простой тест (например, многократно настраивая src изображения) без ajax или обратных вызовов? Вы можете попробовать это с другими браузерами?
Я дал ему поработать несколько часов и пролистал несколько тысяч товаров. Я не мог понять, что это когда-либо освобождает.
Вероятно ли, что это произойдет в реальном мире?
@redsquare Определенно. Это визуализация использования продукта компании. Я ожидаю, что они оставят это на весь день. Изображения выглядят очень красиво, но мне, возможно, придется вытащить их, если браузер взорвется в загруженные дни.
@Scott Safari утекал память медленнее, чем firefox, но я на самом деле не пытался выяснить, что firefox делает с памятью. Хотя я совершенно уверен, что дело в самих изображениях.
Из Центр разработчиков Mozilla на removeChild():
The removed child node still exists in memory, but is no longer part of the DOM. You may reuse the removed node later in your code, via the oldChild object reference.
Это ключ - повторно использовать удаленный дочерний элемент вместо того, чтобы многократно создавать новый.
// I'm sure there is a nicer way to do it in jQuery
if (number_of_things_seen > 10) {
var shots = document.getElementById("shots");
var li = shots.getElementsByTagName("LI");
var move = shots.removeChild(li[li.length-1]);
move.getElementsByTagName("IMG")[0].src = p.ProductImageSmall;
shots.insertBefore(move, li[0]);
}
Это в значительной степени то, что меня заставляло делать redsquare. Это работает, но не предотвращает утечку. : /
Вырастет ли он так же, если вы просто переместите ребенка, не касаясь src?
И - действительно ли вы пробовали нет, используя для этого jQuery (например, попробуйте подход только на JS)? Может быть, сам jQuery все еще как-то отвечает.
Хорошо, я создал для этого тестовую систему, она случайным образом захватывает новое изображение и использует мой код замены src
См. http://pastebin.me/4936458820c43
Я не вижу, чтобы количество изображений в сафари увеличивалось. Полагаю, это неплохо. Я думаю, что это +1 теория xhr. Однако я не совсем понимаю, у кого есть ссылка на img.
Возможно, завтра я попробую irc и посмотрю, сможет ли кто-нибудь сказать мне разницу между тем, что вы делаете, и тем, что делаю я. Спасибо за вашу помощь (пока).
Дастин, Кажется, что оператор insertBefore в моем коде вызывал некоторые проблемы. Если вы видите этот следующий тестовый пример, использование памяти остается довольно статичным, хотя я очищаю последний дочерний элемент и вставляю новый новый фрагмент dom. Работает лучше, чем последняя попытка.
Http://pastebin.me/4936519fea2cf
Является ли след действительно проблемой?
Я подозреваю, что Safari кэширует все изображения, пока вы остаетесь на одном сайте, и вы ничего не можете с этим поделать. Если вам повезет, Safari выпускает ненужные объекты по достижении определенного лимита.
Причина такого поведения в том, что Safari считает вероятным, что эти изображения используются во второй раз, и поэтому кэширует их.
Браузеры печально известны утечками памяти. Похоже, проблема возникает, когда страница остается запущенной в течение длительного времени. Как насчет обновления страницы до того, как ей не хватит памяти?
window.setTimeout("location.reload()",1000*60*60);//refresh in an hour
Можно изменить, чтобы он обновлялся только в том случае, если пользователь ничего не делал в течение 5 минут или около того.
Как я недавно обнаружил, в Firefox есть ошибка, которая вызывает утечку потерянных тегов IMG.
Это можно обойти, переместив изображения в CSS:
if (number_of_things_seen > 10) {
$('#shots li:last-child').remove();
}
$('<li><div style = "background-image: url(\'' + p.ProductImageSmall +
'\'); background-repeat: no-repeat; background-position: center; '+
' width: 20px; height: 20px"></div></li>').prependTo('#shots');
Проверьте, решает ли это вашу утечку памяти.
Вам удалось найти утечку? Мне было бы интересно, если бы вы это сделали.