Комментарии 91
потому что console.log держит ссылку на текст
0
Не в том ли проблема, что каждый раз создается новый объект?
0
На него нет ссылок. Он должен быть вычищен гарбаж-коллектором. Или я не прав в этом?
0
вот этим
xhr.onreadystatechange
вы создаете замыкакание, которое имеет доступ к объявленным переменным, поэтому gc оставляет в памяти переменную xhr+5
хорошо опечатался))
+24
Насколько я понял, Олег уже пробовал обнулять xhr.onreadystatechange в обработчике события. Не помогло.
0
Обнуляя я порушил ссылку, но в области видимости функции остался XHR.
+1
Да, интересная мысль — сам факт обнуления ссылки создал новую ссылку через замыкание. Ну тогда надо еще xhr = null;
0
Не, не создал. Она уже была. Внутренняя анонимная функция видела XHR уже в момент рождения (хоть и не использовала его явно). И каждая новая видела свой XHR.
0
Честно говоря, мне казалось, что в замыкание попадают только те переменные, которые ты явно используешь внутри функции. А то так получится, что ты одной анонимной функцией сходу замыкаешь все переменные во всех доступных сейчас диапазонах видимости.
Это, наверное, легко проверить, если выполнить eval() внутри функции. Будут видны те переменные, на которые не ссылаешься в коде — значит все.
Это, наверное, легко проверить, если выполнить eval() внутри функции. Будут видны те переменные, на которые не ссылаешься в коде — значит все.
-2
Так пойдет? jsfiddle.net/P9A47/
+2
Однозначно.
0
Совершенно не однозначно. Браузеры могут отключать оптимизации когда видят
eval
-1
И похожий вопрос, между прочим, на Хабре рассматривался в статье "Вынеси мусор!"
-1
Тоже резонно. Но я все же думаю, что это слишком необычная ситуация, чтобы браузеры были под нее заточены.
Просто предполагаю.
Просто предполагаю.
0
Евал выполняется очень редко, но когда он выполняется — он должен выполнится корректно. А вот код без евала выполняется часто, потому может быть соптимизирован.
+1
Правильно. И самое прикольное, что мы никак не можем этого проверить. :)
0
Исходники Хромиума и Фокса — открыты, можно обпроверяться)))
0
Это несколько экстремально. :)
А вот косвенный тест можно провести. Сделать кучу замыканий, подобных тому, что у Олега, но без явной ссылки на переменную из наружного скоупа. Сохранять ссылки на фукнции и смотреть потребление памяти.
Если замыкание держит ссылку на неиспользуемый объект, то GC их не сможет освободить. Если же не держит, то сможет.
А вот косвенный тест можно провести. Сделать кучу замыканий, подобных тому, что у Олега, но без явной ссылки на переменную из наружного скоупа. Сохранять ссылки на фукнции и смотреть потребление памяти.
Если замыкание держит ссылку на неиспользуемый объект, то GC их не сможет освободить. Если же не держит, то сможет.
0
я как то сталкивался с тем что при отладке кода в хроме внутри замыкания те переменные которые не используются не были доступны в отладчике, так что можно предположить то что в каких то случаях он не тащит их повсюду :)
+1
По стандарту JavaScript все переменные попадают в замыкание.
+3
Спасибо за информацию.
0
По стандарту они должны попадать, но что, в теории, мешает браузеру соптимизировать переменные, которые точно не будут использоваться? При условии, что поведение будет совпадать со стандартным.
0
Ничего не мешает :)
Но я бы на это полагаться не стал — в том же IE циклические ссылки (элемент-эвент-замыкание на элемент) GC научился собирать только в 9 версии.
Но я бы на это полагаться не стал — в том же IE циклические ссылки (элемент-эвент-замыкание на элемент) GC научился собирать только в 9 версии.
0
Я кстати подозреваю, что именно для этого в стандарте написано, что только прямой вызов eval выполняется в контексте функции (косвенные — в глобальном контексте) — так оптимизировать намного проще.
0
Ссылок то нет, но сообщения обрабатываются, так что здесь вероятно неопределенное поведение и поэтому не удаляет. Ведь в сущности таймаут может быть и с 10 секунд, откуда GC знать когда удалять объект.
0
попробуйте в колбэке обnullить xhr
+1
А разве сама функция, указанная в setInterval не замыкается на контекст, в котором был вызван setInterval? Ведь, по идее, при каждом вызове setInterval будет создаваться новая функция. Я могу ошибаться, и если это так, то хотелось бы знать в чем.
0
Обычно это вызывало утечку в IE из-за вложенной анонимной функции: www.jstoolbox.com/2008/02/12/obnaruzhenie-utechek-pamyati-v-internet-explorer/.
Удаляется функция из памяти через delete <fun_name>;, если нуллить, то объект null. останется.
Удаляется функция из памяти через delete <fun_name>;, если нуллить, то объект null. останется.
+1
При каждом срабатывании интервала будет вызвана одна и та же функция, она не пересоздается заново.
0
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
delete xhr;
}
?
-1
Переменные удалять нельзя ;-)
Обычно делают xhr = null; в таких случаях.
Обычно делают xhr = null; в таких случаях.
+2
А почему нельзя удалять? Какая разница, сделать ее null или undefined?
0
var xyz = function(){
console.log('_xyz_');
delete xyz;
}
xyz();
xyz();
Это сработало как я и ожидал:
_xyz_
ReferenceError: xxxyyyzzz is not defined
BTW. На хабре скрипты что-то в консоль пишут :)
0
Вы это пробовали в FireBug/консоли или скриптом на нормальной странице?
Потому что в первом случае будет работать как вы ожидали (потому что выполняется через eval, а переменные объявленные в eval могут удаляться), а вот во втором — никакого удаления не произойдет (_xyz_ будет записано два раза).
Удалять нельзя не по каким-то там guidlines, а потому что в стандарте так написано — переменные помечаются аттрибутом {DontDelete}.
И еще обратите внимание на такой ньюанс:
Так происходит потому, что b — не переменная, а свойство глобального объекта.
Потому что в первом случае будет работать как вы ожидали (потому что выполняется через eval, а переменные объявленные в eval могут удаляться), а вот во втором — никакого удаления не произойдет (_xyz_ будет записано два раза).
Удалять нельзя не по каким-то там guidlines, а потому что в стандарте так написано — переменные помечаются аттрибутом {DontDelete}.
И еще обратите внимание на такой ньюанс:
var a = 10;
b = 20;
delete a;
delete b;
console.log(a); // 10
console.log(b); // ReferenceError: b is not defined
Так происходит потому, что b — не переменная, а свойство глобального объекта.
+1
НЛО прилетело и опубликовало эту надпись здесь
Всякие там good practices подсказывают, что попробовать надо так:
var callback = function() {
if(this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
}
};
setInterval(function(){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'json.txt', true);
xhr.onreadystatechange = callback;
xhr.send('');
}, 500);
+12
Только хотел предложить подобное решение. Автор, попробуйте, поможет ли это?
+1
Запустил пробовать. Рядом запустил аналог с простым jQ.get
var cb = function(d){ var a = 2 + 3; };
setInterval(function(){
$.get('f.txt', cb);
}, 500);
0
В общем суяд по всему оба варианта текут ГОРАЗДО меньше чем исходный (оба, тот который выше и jQuery.get). Но все-же со временем размер процесса немного пухнет.
Процесс «без jQuery» за первые 40 минут распух с 11 до 21Мб но за следующие 1 час 20 минут не увеличился. Процесс «c jQuery» аналогично — с 8 до 10 и держится.
Процесс «без jQuery» за первые 40 минут распух с 11 до 21Мб но за следующие 1 час 20 минут не увеличился. Процесс «c jQuery» аналогично — с 8 до 10 и держится.
+1
Судя по всему корень зла — повторно создающееся замыкание с видимостью XHR.
0
Тоесть мы минимизируем то, что попало в замыкание к колбэку и попутно вывели инстанс XHR из его области видимости?
0
Имхо, проще так:
setInterval(function(){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'json.txt', true);
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
}
};
xhr.send('');
xhr = null;
}, 500);
0
Похоже, что Google Chrome где-то не удаляет Persistent указатель… вообще очень много в интернете статей про утечки памяти при работе с DOM элементами (они тоже хранятся как Persistent handle)…
+1
В голову почму-то пришла мысль о защите от фрагментации памяти, но походу это бред. Попробуйте использовать jQeury и посмотреть результат. Ждем mraleph в треде, думаю он пояснит в чем тут дело.
0
Вообще есть подозрение что ликает не JS GC, а реализация объекта XMLHttpRequest.
+2
Посмотрите, плиз, течёт ли следующий код?
xhr = new XMLHttpRequest();
xhr.onreadystatechange = callback;
function callback() {
if(xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
function request() {
xhr.open('GET', 'json.txt', true);
xhr.send(null);
}
setInterval(request, 500);
0
А если просто запустить страницу, но не запускать Девелопер-тулз, и смотреть утечку по диспетчеру задач, то есть течь или нет?
0
Даешь ручное управление памятью в js! :)
Или быть может мне подскажет, как бы выполнитить профайлинг жаваскриптового приложения в браузере?
Или быть может мне подскажет, как бы выполнитить профайлинг жаваскриптового приложения в браузере?
0
Я под лисой делал одну оптимизацию для проекта, т.к. память лилась. Допустим что браузер не очень корректно может почистить XMLHttpRequest обьекты. Можно попробовать минимизировать утечки. Решений было несколько.
1. Пул XMLHttpRequest обьектов. Не создавать каждый раз новый, а брать старый из пула если он помечен как свободный.
2. А вы в курсе что происходит с вашими данными и контентом после onComplete? Допустим вы загрузили json файл размером в мегабайт. Так вот этот мегабайт текста будет висеть в памяти внутри XMLHttpRequest обьекта (переменная responseText). Обьект можно очистить, вызвав функцию abort() на нем. Причем ее я вызваю уже после того как совершил все обработки и т.д. onComplete. Собственно это и часть функционала пула, когда ажакс помечен как свободный — делать ему аборт, очищать калбяки и прочие присоски и щупальца :).
3. Ну и понятно что надо следить при таком пуле чуть тщательней за обьектами. Например создал обьект, вызвал, а потом пытаешься вызвать еще раз, а он уже очищен и обнулен. Для таких особых случаев у меня есть флаг, типа не очищать.
1. Пул XMLHttpRequest обьектов. Не создавать каждый раз новый, а брать старый из пула если он помечен как свободный.
2. А вы в курсе что происходит с вашими данными и контентом после onComplete? Допустим вы загрузили json файл размером в мегабайт. Так вот этот мегабайт текста будет висеть в памяти внутри XMLHttpRequest обьекта (переменная responseText). Обьект можно очистить, вызвав функцию abort() на нем. Причем ее я вызваю уже после того как совершил все обработки и т.д. onComplete. Собственно это и часть функционала пула, когда ажакс помечен как свободный — делать ему аборт, очищать калбяки и прочие присоски и щупальца :).
3. Ну и понятно что надо следить при таком пуле чуть тщательней за обьектами. Например создал обьект, вызвал, а потом пытаешься вызвать еще раз, а он уже очищен и обнулен. Для таких особых случаев у меня есть флаг, типа не очищать.
+1
А что если так?
XMLHttpRequest вполне себе reuseble, не обязательно его создавать каждый раз. Повторный вызов send на незаконченный запрос, убьет (сделает abort) незаконченный.
var xhr = new XMLHttpRequest();
xhr.open('GET', 'json.txt', true);
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
console.log(this.responseText);
}
};
setInterval(function(){
xhr.send('');
}, 500);
XMLHttpRequest вполне себе reuseble, не обязательно его создавать каждый раз. Повторный вызов send на незаконченный запрос, убьет (сделает abort) незаконченный.
0
Странно, но никто не заикнулся про использование setTimeout, вместо setInterval.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Публикации
Изменить настройки темы
Простой, казалось бы, вопрос по JavaScript