Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Сборщик мусора в JavaScript (https://javascript.ru/forum/events/1405-sborshhik-musora-v-javascript.html)

freebit 13.07.2008 12:34

Сборщик мусора в JavaScript
 
Ребята, просветите пожалуйста! Если я создал некий элемент, добавил его в DOM, повесил на него слушателя события, а затем удалил этот элемент из DOM вообще, то что будет со слушателем события? Удаляется ли он вместе с элементом сборщиком мусора или продолжает висеть в памяти?

freebits 16.07.2008 02:09

Может вопрос сформулирован неверно?
 
Или ответ настолько очевиден, что каждый прочитавший, ухмыльнувшись встает и идет кормить рыбок:)

Gvozd 16.07.2008 14:53

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

freebit 19.07.2008 21:17

И на этом спасибо!
 
И на этом спасибо!

Gvozd 21.07.2008 15:12

и на том, пожалуйста)

vk65535 21.11.2008 18:23

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

Теперь вопрос: почему эти связи некоторыми товарищами (напр. в Ext) кэшируются и чистятся даже для неослов?

no_alex 27.11.2008 00:21

За Осла судить не берусь, а вот есть у меня один "скриптец" которой я активно юзаю в "3-й лисе".
Я несколько раз специально наблюдал: в процессе работы этого скрипта у меня в течение часа создавалось/убивалось несколько сот объектов с кучей подписок на различные события. При этом объем памяти используемый "лисой" практически не изменялся. Я не уверен насколько это объективно показывает работу "мусорщика"?

Есть какие-нибудь плагины/утилиты которые позволяют все это точно оценивать?

Gvozd 27.11.2008 04:09

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

no_alex 28.11.2008 01:49

Вот такой код для тестирования пойдет:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Test</title>
</head>
<body>
<form action="#" id="test"></form>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
var p, d, f, a, ie, n, i;
p = 50; // Set period of modifying
d = document;
f = d.getElementById("test");
a = [];
ie = /msie/i.test(navigator.userAgent);
n = 0;

window.setInterval(doInput, p);

function doInput()
{
	i = a.length;
	if (!i || Math.random() < 0.5) {
		a[i] = d.createElement("input");
		a[i].setAttribute("value", n++);
		if (ie) {
			a[i].attachEvent('onclick', e1);
			a[i].attachEvent('onmousedown', e2);
			a[i].attachEvent('onmouseup', e3);
		} else {
			a[i].addEventListener("click", e1, false);
			a[i].addEventListener("mousedown", e2, false);
			a[i].addEventListener("mouseup", e3, false);
		}
		f.appendChild(a[i]);
	} else {
		f.removeChild(a.shift());
	}
}

function e1()
{
	//alert("Event 1");
}
function e2()
{
	alert("Event 2");
}
function e3()
{
	alert("Event 3");
}

//--><!]]>
</script>
</body></html>


Вот только хотелось бы понять методику для тестирования - что надо проверять окромя "съеденной" браузером памяти?
Или "измеренной памяти" достаточно для объективных выводов?

Octane 28.11.2008 03:21

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

Вот во время написания фреймворка, я столкнулся с проблемой: при удалении узла через removeChild, в памяти остаются все дочерние узлы, на которые где-либо хранятся ссылки:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script type="text/javascript">
window.onload = function() {
	// Test 1
	var div = document.getElementById('test1');
	document.body.removeChild(document.getElementById('conteiner1'));
	alert(div.id);
	
	// Test 2
	var div = document.getElementById('test2'), cont = document.getElementById('conteiner2');
	while(cont.firstChild) cont.removeChild(cont.firstChild);
	document.body.removeChild(cont);
	alert(div.id);
		
	// Test 3
	document.body.removeChild(document.getElementById('conteiner3'));
	alert(document.getElementById('test3'));
};
</script>
</head>
<body>
	<div id="conteiner1">
		<div id="test1">Test</div>
	</div>

	<div id="conteiner2">
		<div id="test2">Test</div>
	</div>
	
	<div id="conteiner3">
		<div id="test3">Test</div>
	</div>
</body>
</html>

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

Это конечно не баг, а особенность, но даже метод removeNode в IE, который принимает параметр булевского типа, для явного указания того, что дочерние узлы должны быть удалены, в случае наличия ссылки на элемент, не делает этого :(

Впрочем, так ведет себя любой объект — остается в памяти, пока есть хоть одна ссылка на него
var obj = {a: 'aa'}, c = obj;
obj = null;
alert(c.a);


Кстати, чтобы оставить в памяти удаляемый узел, нужно написать так
var removedNode = parent.removeChild(node);

no_alex 28.11.2008 03:55

Цитата:

И что этим кодом можно увидеть? Скачки в несколько килобайт используемой памяти, связаны с работой сборщика мусора. Не в одном браузере утечек памяти не наблюдается.
Именно в этом я и хотел убедится. У меня, пока тоже, аналогичный результат получился.

Цитата:

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

Другое дело, не произойдёт ли что-либо подобное, если на чилдрена есть не прямая ссылка, а подписка на событие. Мне сегодня уже лень это проверять - завтра попробую протестить.
Или если у кого-то будет еще желание этим заняться - бросьте сюда тестовый код.

no_alex 28.11.2008 04:08

Небольшой оффтопик, с Вашего позволения...

Цитата:

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

Или Вы в этом случае запрещаете кэширование? Тогда, ИМХО, его надо будет запрещать в 90% случаев. А в оставшихся 10% эффективность кэша будет близка к нулю.

Это по крайней мере было-бы так с теми задачами, которые мне приходилось решать.

Octane 28.11.2008 04:28

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

vk65535 28.11.2008 19:37

Цитата:

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

Протестить можно, допустим, так:
Код:

for (var i = 0,
        efn = window.addEventListener ? 'addEventListener' : 'attachEvent',
        et = (window.addEventListener ? '' : 'on') + 'click',
        e; i < 100000; ++i
)
        (e = document.createElement('DIV'))[efn](et, function(e){ return function(){}; }(e), false);

Лиса вначале ест память, потом, если дать ей немного остыть - отдает.
Опера-9 довольно быстро и корректно отрабатывает сборку мусора, отъем памяти почти незаметен.
Осел, ест, но, как и ожидалось, не отдает. Если убрать циклическую ссылку из замыкания (выделена жирным), то, осел вообще не ест память (либо отдает слишком быстро). Если свернуть окно осла, память освобождается, но если потом опять развернуть и еще разок прогнать цикл - достает всю кучу мусора из свопа и продолжает ее наполнять )
Сафаря-3, память отдавать не торопится - или это похожий баг, или там уборщик мусора ленивый )

Gvozd 04.12.2008 23:58

vk65535, Большое спасибо, за тесты


Часовой пояс GMT +3, время: 21:07.