Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Сборщик мусора. Удаляется объект, который в дисплей листе. (http://www.flasher.ru/forum/showthread.php?t=192575)

Волгоградец 16.01.2013 12:26

Сборщик мусора. Удаляется объект, который в дисплей листе.
 
Привет. Заголовок немного желтоват. На самом деле код выглядит так:

Код AS3:

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 находится в дисплей листе. Собственно вопрос - какой именно из этих объектов уничтожается и почему?

toFL 16.01.2013 12:32

Ну так все правильно. После вот этого:
Код AS3:

var a:A = new A();
addChild(a.vis);

"a.vis" будет живет долго и счастливо, так как он в дисплей листе. А вот "a" умирает, так как на него нет ни одной ссылки. GC удаляет не целостные объекты и не подмножества...а те на которые нет ссылок.

Волгоградец 16.01.2013 12:47

Но если он его удалит, значит он должен удалить все его поля, в том числе и vis. Т.е. память, занимаемая объектами должна освободиться. Но vis на месте и я могу обращаться к нему. А что если позже что-то перезапишется на эту память?

Wolsh 16.01.2013 12:53

Не путайте ссылку и значение. Ссылка будет удалена, а объект останется, так как на него есть другая ссылка — в контейнере.

Добавлено через 7 минут
Код AS3:

public var vis:Sprite = new Sprite();

public var vis:Sprite — это переменная, хранящая ссылку. Это не сам спрайт. Это ссылка, где его искать.
new Sprite(); — вот собственно "сам" спрайт, создаваемый где-то в памяти. Где — сохраняется в переменной. Или не сохраняется явно, а только во внутреннем хэше контейнера:
Код AS3:

addChild(new Sprite());


Волгоградец 16.01.2013 13:03

Wolsh, не понял. Если срабатывает ГЦ, то он именно удаляет объекты. Вот еще пример.

Код AS3:

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 будет работать бесконечно долго. В чем разница между ними?

expl 16.01.2013 13:28

Цитата:

Сообщение от Волгоградец (Сообщение 1115912)
Wolsh, не понял. Если срабатывает ГЦ, то он именно удаляет объекты.

Примерно можно использовать такую модель:
Объект - это кусок памяти, в котором лежат:
- вспомогательная информация
- ссылки на другие объекты (физически 4 байта)
- примитивные типы (int, Number) - физически 4, 8 байт
И вот это всё лежит в линеечку (для одного объекта)

Ссылки ссылаются на другие объекты. Т.е. удалить объект - это удалить как раз ссылки на другие и примитивные значения.

Объекты и их ссылки представляют собой не _дерево_, а _граф_
Здесь нельзя просто взять и удалить вместе с объектом те, на которые он ссылается. На эти ссылочные объекты может ссылаться кто-то ещё.

По поводу примера кода - не смотрел ещё, вообще странно.

Волгоградец 16.01.2013 13:35

Цитата:

Здесь нельзя просто взять и удалить вместе с объектом те, на которые он ссылается. На эти ссылочные объекты может ссылаться кто-то ещё.
Вот здесь немного проясняется. Я думал, что ГЦ просто не может убить объект, внутри которого есть "живые" объекты. Оказывается может.

Пример, да, странный. У меня еще парочка необъяснимых имеется.

iflamberg 16.01.2013 14:40

Пример не странный, пример реальный. Я впервые столкнулся с таким удалением, когда делал игрушку, которая пачкой loader'ов загружала множество кусочков карты по 100 на 100 пикселей. Так вот я просто создавал в теле функции loader'ы и сразу кидал на сцену, нигде не хранил. И на некоторых компьютерах я увидел, что объекты лоадера ГЦ съедает даже еще до того, как картинка загрузилась. Причем, не очень уже хорошо помню, давно это было, но уверен, что на все загрузчики были навешаны слушатели, и не weak, и все равно ГЦ их съедал. Решил проблему хранением массива этих загрузчиков в классе, пока они все не загрузились.

expl 16.01.2013 16:00

Цитата:

на все загрузчики были навешаны слушатели, и не weak, и все равно ГЦ их съедал.
Всё правильно, он не сьёдал бы их, только если бы слушатель принадлежал самому лоадеру и Вы бы повесили его слушатель на объект, который не подлежал бы сборке мусора.

А так вы навесили слушатель - т.е. добавили ссылку на себя в лоадер - как это должно помешать его сборке?

По порядку:
Код AS3:

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.
Код AS3:

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).

dimarik 16.01.2013 17:38

Там то же самое. Ссылка на A гибнет в конструкторе Main. Для A#s листенер объявлен со слабой ссылкой, которая является последней зацепкой за GC Root. Финал понятен.

Добавлено через 1 минуту
"s" как раз будет жить, гибнет A.

Волгоградец 16.01.2013 18:11

Гибнет А и получается, что хэндлер вместе с ним. Такое возможно? Ведь на хэндлер ссылается A#s. И пускай ссылка слабая, сам же A#s то живой.

dimarik 16.01.2013 18:24

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

Ну так он же в дисплей листе ). Блин запутанно как то все...

dimarik 16.01.2013 22:23

Цитата:

Сообщение от Волгоградец (Сообщение 1115983)
С тем, что А убивается коллектором я уже смирился. Но хэндлер повешен не на А. Почему этот хэндлер то убивается?

Хэндлер (метод enterFrameHandler) убиться без его хозяина (объекта типа A) не может в принципе. Он является полем объекта и разлучить его с объектом мы не в состоянии. Нельзя просто так сделать delete enterFrameHandler. Убивается А, а вместе с ним и его метод enterFrameHandler. Как вы думаете, если нет объекта, может ли быть вызван его метод?

Волгоградец 17.01.2013 10:56

Так вроде как метод - это Function - обычный объект, наследник Object. Убился А, хозяин s, но сам то s жив, потому что на него ссылка в дисплей листе. Но ведь и на enterFrameHandler есть ссылка из s, в конце концов s и enterFrameHandler связаны... И раз уж enterFrameHandler - это обычный объект, то он то почему должен убиваться?

dimarik 17.01.2013 11:39

Чтобы объект убился необходимо чтобы не осталось на него ни одной ссылки. Еще раз...
Объект удаляется, если на него, на него, на него нет ссылок.
Считайте ссылки на объект 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, потому что он в дисплей листе, правильно?

dimarik 17.01.2013 13:00

A = enterFrameHandler в данном случае. Поэтому у s есть слабая ссылка на A.

Волгоградец 17.01.2013 13:03

Если это действительно так, то все становится на свои места. Хотя это специфическая инфа, которую я не встречал. Спасибо.

dimarik 17.01.2013 13:08

"A" ссылается на enterFrameHandler. enterFrameHandler ссылается на "A". Но на enterFrameHandler не ссылается никто из имеющих GC Root. Приходим к тому, что на "А" нет ссылок. "A" молча уходит со своими методами.

Волгоградец 17.01.2013 13:17

Вас опять не понятно. Вот эта строчка
Код AS3:

s.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);

говорит, что на enterFrameHandler ссылается s. weak reference = true говорит о том, что enterFrameHandler будет срабатывать до тех пор, пока жив s. Или я опять туплю - когда мы вызываем addEventListener, мы связываем s и А, так что ли???

P.S.: не подумайте, что я тролль. Мне нужно докопаться до истины и сложить паззл у себя в голове.

expl 17.01.2013 13:40

Цитата:

Или я опять туплю - когда мы вызываем addEventListener, мы связываем s и А, так что ли???
Если говорить без мягких ссылок:

a.addEventListener("typeOfEvent", b.handler)
-теперь a ссылается на метод b
-b _не_ ссылается на a
-эта связь помешает удалению b из памяти, когда на b не останется других ссылок
-эта связь никак не помешает удалению a из памяти

Если с мягкими ссылками - то "a ссылается на метод b" перестаёт учитыватся GC вот и всё.

GC удаляет объект, когда на него не осталось внешних ссылок (ссылок из дисплей-листа, ссылок из статических полей классов, и т.д.)

Ссылки от объектов, которые сами подлежат удалению GC не считаются. Что такое Объекты, которые подлежат удалению? - это объекты на которые нет ссылок объектов, _не_ подлежащих удалению.
"Чтобы понять рекурсию надо понять рекурсию" :)

dimarik 17.01.2013 13:43

Stage (GCRoot) --> внутренний список детей --> [object Sprite] --> внутренний список хендлеров -x-> (enterFrameHandler --> [object A])

--> — обычная ссылка.
-x-> — мягкая ссылка.

maxkar 17.01.2013 14:56

Цитата:

Сообщение от Волгоградец (Сообщение 1116178)
weak reference = true говорит о том, что enterFrameHandler будет срабатывать до тех пор, пока жив s.

Нет! Никакого отношения к s эта строчка не имеет. Она обозначает, что слушатель будет срабатывать до тех пор, пока живсам слушатель (а не прослушиваемый объект)! Не s (объект, на котором слушают), а метод, который слушает. При этом GC связь между s и listener не учитывает, т.е. он не считает, что s использует enterFrameHandler и может собирать функцию (и объект), если на них нет других ссылок.

Волгоградец 17.01.2013 15:56

Все ясно. Всем спасибо.


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

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