Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Good

Рейтинг: 5.00. Голосов: 2.

Подводные камни Dictionary

Запись от artcraft размещена 04.09.2012 в 20:23
Обновил(-а) artcraft 05.09.2012 в 20:36

Dictionary прекрасная штука, но его документация умалчивает о двух подводных камнях не знание о которых чревато утечками памяти или потерей данных

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

Код AS3:
package  {
	import flash.display.Sprite;
	import flash.system.System;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;
 
	public class MemTest extends Sprite {
		private var d:Dictionary = new Dictionary(true);
 
		public function MemTest() {
			d[m] = 'test';
			count(); //1
			System.gc();
			setTimeout(count, 1000); //0
		}
 
		public function count():void {
			var n:uint=0, o:Object
			for (o in d){ n++; }
			trace(n);
		}
 
		public function m():void {}		
	}
}
В интернете пишут что это баг, но такое поведение можно объяснить:
дело в том что вместо метода передаётся ссылка на MethodClosure - специальный нативный скрытый тип, хранящий ссылку на хозяина оригинального метода и отвечающий за область видимости, но вот у хозяина на MethodClosure ссылки нет, отсюда и такое поведение Dictionary

2. если в словаре со слабыми ссылками в качестве значения присвоить тот самый объект который является ключом, то, если других ссылок на этот объект нету, пара ключ-значение удалению сборщиком мусора не подлежит.
(запись не удаляется когда кажется что должна)
Код AS3:
package  {
	import flash.display.Sprite;
	import flash.system.System;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;
 
	public class MemTest extends Sprite {
		private var d:Dictionary = new Dictionary(true);
		private var a:A;
 
		public function MemTest() {
			a = new A();
			d[a] = a;			
			count(); //1
 
			a = null;
			System.gc();
			setTimeout(count, 1000); //1
		}
 
		public function count():void {
			var n:uint=0, o:Object
			for (o in d){ n++; }
			trace(n);
		}		
	}
}
class A {}
Логика такая - при подсчёте ссылок на объект сборщик мусора выясняет что на него есть одна жёсткая ссылка - в значении записи в словаре, поэтому ключ удалять нельзя и он будет жить в памяти. Cборщик мусора мог быть умнее и уметь удалять такое, но он не умеет.
Всего комментариев 15

Комментарии

Старый 04.09.2012 20:56 fljot вне форума
fljot
Мне вот ещё до сих пор не ясен момент: можно ли, проходя по ключам словаря, удалять их? Вроде как и тесты не фейлятся, а вроде как и боязно.

Код AS3:
for (var key:* in dict)
{
    delete dict[key];
}
Старый 04.09.2012 22:09 artcraft вне форума
artcraft
 
Аватар для artcraft
проще сделать так:
dict = new Dictionary();
Старый 04.09.2012 23:50 TanaTiX вне форума
TanaTiX
 
Аватар для TanaTiX
Цитата:
проще сделать так:
Не проще, т.к. объект остается, но ссылка уже ссылается на другой, новый, объект.
Старый 04.09.2012 23:56 fljot вне форума
fljot
Ну да, можно просто создать новый, но я хотел минимизировать вызовы GC.
Старый 05.09.2012 00:12 TanaTiX вне форума
TanaTiX
 
Аватар для TanaTiX
Код AS3:
delete dict[key];
У меня такой подход еще проблем не вызывал. Правда я и не извращался, как описывает artcraft
Старый 05.09.2012 02:07 artcraft вне форума
artcraft
 
Аватар для artcraft
насколько я знаю delete не очищает память а удаляет ссылку, поэтому в любом случае сборщику мусора будет работа

но я не уверен какой из способов займёт меньше сил у GC, и, возможно я не прав, но мне кажется это не сильно существенным.

в AS3 есть только два метода которые непосредственно очищают память BitmapData::dispose() и disposeXML()
Обновил(-а) artcraft 05.09.2012 в 02:23
Старый 05.09.2012 11:56 -De- вне форума
-De-
 
Аватар для -De-
По-моему стоит предложить и workaround 1-й проблемы - сохранять ссылку на функцию в переменной типа Function. Думаю такая ссылка и будет ссылкой (в т.ч.?) на MethodClosure. Второе - нормальное поведение. Да, плохо, что нет нормальных слабых ссылок. Их можно сделать, но через плохое место %)
Старый 05.09.2012 14:01 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Цитата:
Мне вот ещё до сих пор не ясен момент: можно ли, проходя по ключам словаря, удалять их? Вроде как и тесты не фейлятся, а вроде как и боязно.
Я бы не стал. Потому как Dictionary - это по сути HashMap<Object, Object>. А HashMap по определению неупорядочен. Я не знаю точно как он реализован в AVM2, но повторюсь, рисковать не стал бы.

Цитата:
насколько я знаю delete не очищает память а удаляет ссылку, поэтому в любом случае сборщику мусора будет работа
Ну ему останется только пометить область памяти как свободную. Потому как имеют место одновременно 2 алгоритма сборки мусора:
1) подсчет ссылок (и, если их 0, то однозначно удаляем объект)
2) построение дерева ссылок (тут как раз решается вопрос с циклическими ссылками)

Так что, в общем случае, ручное удаление ссылок должно снижать нагрузку на GC.
Старый 05.09.2012 15:57 artcraft вне форума
artcraft
 
Аватар для artcraft
Для 100% надёжного обнуления всех записей в Dictionary google предложил вот такую функцию:

Код AS3:
function clearDictionary(d:Dictionary):void{
    //Get Keys from dictionary.
    var idVec:Vector.<Object> = new Vector.<Object>(0);
    for(var obj:Object in d){
        idVec.push(obj);
    }
 
    //Delete Keys from dictionary and clear from vector at same time.
    var vLen:int = idVec.length;
    for(var vi:int = 0; vi<vLen; vi++){
        delete d[ idVec[vi] ];
        idVec[vi] = null;
    }
}
Старый 05.09.2012 18:13 i.o. вне форума
i.o.
 
Аватар для i.o.
Можно тогда и так:
Код AS3:
function clearDictionary(d:Dictionary):void
{
	var b:Boolean = true;
	while (b)
	{
		b = false;
		for (var p:* in d)
		{
			b = true;
			delete d[p];
			break;
		}
	}
}
Старый 05.09.2012 20:22 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Строчка a = null - это nop, она ни на что не влияет, возможно даже наш не особо оптимизирующий компиляторй ее выбросит. По поводу удаления - хз, на сколько это баг, нужно смотреть в релизном плеере и пытаться забить память до отказа такими несобранными объектами. Может это баг в System.gc() а не в словаре.
Старый 05.09.2012 20:33 artcraft вне форума
artcraft
 
Аватар для artcraft
>> i.o.
отличный вариант
только определение переменной р стоит вынести за пределы циклов, и задать ей тип Object

>> wvxvw
о да, это я погорячился обнулять локальную переменную, сейчас поправлю

обе вещи которые я описал, багами не считаю, это просто не слишком очевидное поведение мягких ссылок которое может привести к неприятным последствиям
Обновил(-а) artcraft 05.09.2012 в 20:48
Старый 07.09.2012 12:55 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Не, это просто циклическая зависимость, но если мусорщик ее не обнаруживает - это баг, должен обнаруживать. Это в какой-то степени могло быть легитимно, если бы конструктор у А был с побочным эффектом, сохраняющим себя куда-нибудть, типа:
Код AS3:
public function A() {
    super();
    A.foo = this;
}
Тогда - да (таймер почти наверняка что-то такое делает). А в другом случае - баг.
Старый 07.09.2012 14:42 artcraft вне форума
artcraft
 
Аватар для artcraft
можно называть это багом или отсутствующей фичей, но факт в том что мусоросборщик не умеет распознавать циклические зависимости если в порочном круге встречается слабая ссылка Dictionary
Старый 10.12.2012 21:24 incvizitor вне форума
incvizitor
 
Аватар для incvizitor
Цитата:
если в качестве ключа в словаре со слабыми ссылками использовать метод, то эта запись подлежит удалению сборщиком мусора вне зависимости от того есть другие ссылки на хозяина метода или нет.
А как же реализована слабая связанность в событиях?
 

 


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


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.