Просмотр полной версии : Сборщик мусора. Удаляется объект, который в дисплей листе.
Волгоградец
16.01.2013, 12:26
Привет. Заголовок немного желтоват. На самом деле код выглядит так:
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
public class GCTest extends Sprite {
public function GCTest() {
stage.addEventListener(MouseEvent.CLICK, stageMouseClickHandler);
}
private function stageMouseClickHandler(event:MouseEvent):void {
var a:A = new A();
addChild(a.vis);
}
}
}
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.system.System;
internal class A {
public var vis:Sprite = new Sprite();
private const _SHAPE:Shape = new Shape();
function A() {
vis.graphics.beginFill(0xFF0000);
vis.graphics.drawCircle(0, 0, 50);
vis.graphics.endFill();
_SHAPE.addEventListener(Event.ENTER_FRAME, entarFrameHandler);
}
private function entarFrameHandler(event:Event):void {
trace('enter frame handler');
System.gc();
}
}
При клике по сцене создается объект... и тут же удаляется сборщиком мусора. Насколько я знаю принцип работы ГЦ, объект не уничтожается, если есть хотя бы одна ссылка на него. Что мы имеем тут? _SHAPE находится не в дисплей листе, но является полем класса A. vis также является полем класса A. Сам класс A наследник Object и объект этого класса создается в локальной области видимости, на него ссылок нет, т.е. он явный кандидат на уничтожение. НО! vis, который является дисплей обжектом и полем класса A находится в дисплей листе. Собственно вопрос - какой именно из этих объектов уничтожается и почему?
Ну так все правильно. После вот этого:
var a:A = new A();
addChild(a.vis);
"a.vis" будет живет долго и счастливо, так как он в дисплей листе. А вот "a" умирает, так как на него нет ни одной ссылки. GC удаляет не целостные объекты и не подмножества...а те на которые нет ссылок.
Волгоградец
16.01.2013, 12:47
Но если он его удалит, значит он должен удалить все его поля, в том числе и vis. Т.е. память, занимаемая объектами должна освободиться. Но vis на месте и я могу обращаться к нему. А что если позже что-то перезапишется на эту память?
Не путайте ссылку и значение. Ссылка будет удалена, а объект останется, так как на него есть другая ссылка — в контейнере.
Добавлено через 7 минут
public var vis:Sprite = new Sprite();
public var vis:Sprite — это переменная, хранящая ссылку. Это не сам спрайт. Это ссылка, где его искать.
new Sprite(); — вот собственно "сам" спрайт, создаваемый где-то в памяти. Где — сохраняется в переменной. Или не сохраняется явно, а только во внутреннем хэше контейнера: addChild(new Sprite());
Волгоградец
16.01.2013, 13:03
Wolsh, не понял. Если срабатывает ГЦ, то он именно удаляет объекты. Вот еще пример.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.system.System;
public class GCTest extends Sprite {
public function GCTest() {
stage.addEventListener(MouseEvent.CLICK, stageMouseClickHandler);
}
private function stageMouseClickHandler(event:MouseEvent):void {
var a:A = new A();
addChild(a.s);
var s:Sprite = new Sprite();
addChild(s);
s.addEventListener(Event.ENTER_FRAME, test, false, 0, true);
}
private function test(event:Event):void {
trace('inside main');
System.gc();
}
}
}
import flash.display.Sprite;
import flash.events.Event;
import flash.system.System;
internal class A {
public var s:Sprite = new Sprite();
function A() {
s.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
s.graphics.beginFill(0x00FF00);
s.graphics.drawCircle(0, 0, 50);
s.graphics.endFill();
}
private function enterFrameHandler(event:Event):void {
trace('inside A');
System.gc();
}
}
Тут еще интересней. s, что внутри A удалится сразу же, хотя он в дисплей листе. s, что внутри Main будет работать бесконечно долго. В чем разница между ними?
Wolsh, не понял. Если срабатывает ГЦ, то он именно удаляет объекты.
Примерно можно использовать такую модель:
Объект - это кусок памяти, в котором лежат:
- вспомогательная информация
- ссылки на другие объекты (физически 4 байта)
- примитивные типы (int, Number) - физически 4, 8 байт
И вот это всё лежит в линеечку (для одного объекта)
Ссылки ссылаются на другие объекты. Т.е. удалить объект - это удалить как раз ссылки на другие и примитивные значения.
Объекты и их ссылки представляют собой не _дерево_, а _граф_
Здесь нельзя просто взять и удалить вместе с объектом те, на которые он ссылается. На эти ссылочные объекты может ссылаться кто-то ещё.
По поводу примера кода - не смотрел ещё, вообще странно.
Волгоградец
16.01.2013, 13:35
Здесь нельзя просто взять и удалить вместе с объектом те, на которые он ссылается. На эти ссылочные объекты может ссылаться кто-то ещё.
Вот здесь немного проясняется. Я думал, что ГЦ просто не может убить объект, внутри которого есть "живые" объекты. Оказывается может.
Пример, да, странный. У меня еще парочка необъяснимых имеется.
iflamberg
16.01.2013, 14:40
Пример не странный, пример реальный. Я впервые столкнулся с таким удалением, когда делал игрушку, которая пачкой loader'ов загружала множество кусочков карты по 100 на 100 пикселей. Так вот я просто создавал в теле функции loader'ы и сразу кидал на сцену, нигде не хранил. И на некоторых компьютерах я увидел, что объекты лоадера ГЦ съедает даже еще до того, как картинка загрузилась. Причем, не очень уже хорошо помню, давно это было, но уверен, что на все загрузчики были навешаны слушатели, и не weak, и все равно ГЦ их съедал. Решил проблему хранением массива этих загрузчиков в классе, пока они все не загрузились.
на все загрузчики были навешаны слушатели, и не weak, и все равно ГЦ их съедал.
Всё правильно, он не сьёдал бы их, только если бы слушатель принадлежал самому лоадеру и Вы бы повесили его слушатель на объект, который не подлежал бы сборке мусора.
А так вы навесили слушатель - т.е. добавили ссылку на себя в лоадер - как это должно помешать его сборке?
По порядку:
private function stageMouseClickHandler(event:MouseEvent):void {
var a:A = new A();
addChild(a.vis);
}
1. Вы создали экземпляр A
2. Вы взяли у экземпляра A vis и добавили его в список отображения
3. Вы нигде не сохранили ссылку на сам экземпляр A (функция отработала - ссылок на экземпляр A не осталось, остался только vis в списке отображения)
Что должно получиться:
- ничего не мешает собрать GC экземпляр A
- GC не может собрать vis.
public var vis:Sprite = new Sprite();
private const _SHAPE:Shape = new Shape();
function A() {
vis.graphics.beginFill(0xFF0000);
vis.graphics.drawRect(-25, -25, 50, 50);
vis.graphics.endFill();
_SHAPE.addEventListener(Event.ENTER_FRAME, entarFrameHandler);
}
3. Вы вешаете слушатель не на vis, а на _SHAPE
4. _SHAPE на сцену не добавляется
5. на _SHAPE ссылается экземпляр A, на который вы не сохранили никаких ссылок
Что должно произойти:
- Экземпляр _SHAPE должен быть снесён сборщиком мусора
Вывод: Хороший пример, демонстрирующи работу GC и никакой мистики!
iflamberg
16.01.2013, 16:18
Да, хороший пример, в мемориз однозначно.
Волгоградец
16.01.2013, 17:12
Да, с первым понятно, спасибо. Повторю - я не знал, что ГЦ убивает объект, но трогает его поля.
Но как быть со вторым примером (пост 5).
Там то же самое. Ссылка на A гибнет в конструкторе Main. Для A#s листенер объявлен со слабой ссылкой, которая является последней зацепкой за GC Root. Финал понятен.
Добавлено через 1 минуту
"s" как раз будет жить, гибнет A.
Волгоградец
16.01.2013, 18:11
Гибнет А и получается, что хэндлер вместе с ним. Такое возможно? Ведь на хэндлер ссылается A#s. И пускай ссылка слабая, сам же A#s то живой.
A#s живой, верно. Но на A есть только одна ссылка через метод enterFrameHandler. Он добавлен во внутренний список хендлеров объекта Sprite (s). Хендлер добавлен слабой ссылкой. Это значит, что если нет ни одной твердой ссылки на объект A, ссылка на него через enterFrameHandler не препятствует сборщику удалить этот самый A.
Волгоградец
16.01.2013, 18:40
Это значит, что если нет ни одной твердой ссылки на объект A
С тем, что А убивается коллектором я уже смирился. Но хэндлер повешен не на А. Почему этот хэндлер то убивается?
iflamberg
16.01.2013, 19:27
Так а почему бы ему не убиться, если он weak?
Волгоградец
16.01.2013, 22:13
Ну так он же в дисплей листе ). Блин запутанно как то все...
С тем, что А убивается коллектором я уже смирился. Но хэндлер повешен не на А. Почему этот хэндлер то убивается?
Хэндлер (метод enterFrameHandler) убиться без его хозяина (объекта типа A) не может в принципе. Он является полем объекта и разлучить его с объектом мы не в состоянии. Нельзя просто так сделать delete enterFrameHandler. Убивается А, а вместе с ним и его метод enterFrameHandler. Как вы думаете, если нет объекта, может ли быть вызван его метод?
Волгоградец
17.01.2013, 10:56
Так вроде как метод - это Function - обычный объект, наследник Object. Убился А, хозяин s, но сам то s жив, потому что на него ссылка в дисплей листе. Но ведь и на enterFrameHandler есть ссылка из s, в конце концов s и enterFrameHandler связаны... И раз уж enterFrameHandler - это обычный объект, то он то почему должен убиваться?
Чтобы объект убился необходимо чтобы не осталось на него ни одной ссылки. Еще раз...
Объект удаляется, если на него, на него, на него нет ссылок.
Считайте ссылки на объект A:
1. В конструкторе создается локальная ссылка. При выходе из него она умирает.
2. У s есть на него мягкая ссылка через enterFrameHandler. Она не играет роли при подсчете ссылок на объект.
Все. Коллектор удалит этот объект. Вы извините меня, но я уже не знаю как проще рассказать.
Волгоградец
17.01.2013, 12:46
2. У s есть на него мягкая ссылка через enterFrameHandler. Она не играет роли при подсчете ссылок на объект.
Вы мне вот это объясните:
1. У s есть ссылка не на A, а на enterFrameHandler. A и enterFrameHandler - это разные объекты. Да, enterFrameHandler является приватным методом A, но тем не менее, это разные объекты, правильно я понимаю?
2. Вы утверждаете, что при удалении А, удалится и его метод enterFrameHandler (хоть он и связан с s), так?
3. Но s не удалится при удалении A, потому что он в дисплей листе, правильно?
A = enterFrameHandler в данном случае. Поэтому у s есть слабая ссылка на A.
Волгоградец
17.01.2013, 13:03
Если это действительно так, то все становится на свои места. Хотя это специфическая инфа, которую я не встречал. Спасибо.
"A" ссылается на enterFrameHandler. enterFrameHandler ссылается на "A". Но на enterFrameHandler не ссылается никто из имеющих GC Root. Приходим к тому, что на "А" нет ссылок. "A" молча уходит со своими методами.
Волгоградец
17.01.2013, 13:17
Вас опять не понятно. Вот эта строчка
s.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
говорит, что на enterFrameHandler ссылается s. weak reference = true говорит о том, что enterFrameHandler будет срабатывать до тех пор, пока жив s. Или я опять туплю - когда мы вызываем addEventListener, мы связываем s и А, так что ли???
P.S.: не подумайте, что я тролль. Мне нужно докопаться до истины и сложить паззл у себя в голове.
Или я опять туплю - когда мы вызываем addEventListener, мы связываем s и А, так что ли???
Если говорить без мягких ссылок:
a.addEventListener("typeOfEvent", b.handler)
-теперь a ссылается на метод b
-b _не_ ссылается на a
-эта связь помешает удалению b из памяти, когда на b не останется других ссылок
-эта связь никак не помешает удалению a из памяти
Если с мягкими ссылками - то "a ссылается на метод b" перестаёт учитыватся GC вот и всё.
GC удаляет объект, когда на него не осталось внешних ссылок (ссылок из дисплей-листа, ссылок из статических полей классов, и т.д.)
Ссылки от объектов, которые сами подлежат удалению GC не считаются. Что такое Объекты, которые подлежат удалению? - это объекты на которые нет ссылок объектов, _не_ подлежащих удалению.
"Чтобы понять рекурсию надо понять рекурсию" :)
Stage (GCRoot) --> внутренний список детей --> [object Sprite] --> внутренний список хендлеров -x-> (enterFrameHandler --> [object A])
--> — обычная ссылка.
-x-> — мягкая ссылка.
weak reference = true говорит о том, что enterFrameHandler будет срабатывать до тех пор, пока жив s.
Нет! Никакого отношения к s эта строчка не имеет. Она обозначает, что слушатель будет срабатывать до тех пор, пока живсам слушатель (а не прослушиваемый объект)! Не s (объект, на котором слушают), а метод, который слушает. При этом GC связь между s и listener не учитывает, т.е. он не считает, что s использует enterFrameHandler и может собирать функцию (и объект), если на них нет других ссылок.
Волгоградец
17.01.2013, 15:56
Все ясно. Всем спасибо.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.