8
votes

Ce qui garde les objets scopés localement vivants lorsque les auditeurs sont enregistrés?

Je vois ce type de magie apparente dans toutes sortes de code AS3, mais voici un exemple réduit: xxx

comme indiqué dans le commentaire du code, le chargeur > La variable doit tomber hors de portée après le UrlloaderExample constructeur. Cependant ... il semble toujours être maintenu en vie (non déchets collectés) quelque part depuis que le OnComplete Listenter / Handler est capable de le recevoir proprement.

Où est le Magic / caché / Global Référence à Loader qui le garde en vie, de sorte qu'il puisse terminer son fonctionnement de charge, puis être remis à la OnComplete Listenter / Callback? Peut-on voir cette référence quelque part?

Pour aider au contexte ... comme un exemple similaire, je sais que l'instance loader aura le Oncomplete auditeur enregistré. Je sais aussi que j'ai besoin de faire attention à utiliser refelevereventListener en tout temps (?) Pour éviter les fuites de mémoire potentielles résultant d'auditeurs bloqués. Ce qui me préoccupe, c'est que je ne comprends pas où le chargeur magique est et si (ou lorsque), je dois nettoyer cela.

est-ce peut-être le loader.load () Appeler lui-même ce bourrant chargeur quelque part globalement?


2 commentaires

@NOX NOCTIS - Je vais certainement le faire. Pas besoin de nag. :) Cependant ... été occupé et je ne suis pas sûr de la façon dont je vais continuer. Je n'aime pas accepter des réponses pseudo-bonnes. Le vôtre est 100% correct et utile RE: GC et Scoping général. Bien que pour l'affaire Urlloader spécifique, J_A_X est probablement correct (les tests consommeraient du temps) sur le référencement interne du vaudou. Quand je reviens au développement flash, je l'examinerai plus, éventuellement fusionnant les réponses.


@NOX NOCTIS - ou ... je pourrais accepter la réalité et que je n'aurais probablement pas le temps de tester complètement l'exploitation de référence magique / cachée notée par J_A_X ... Je l'ai fait et accepté votre réponse avec un commentaire .


4 Réponses :


1
votes

Lorsque vous ajoutez un auditeur d'événements, vous créez implicitement une référence à l'objet Loader (par défaut). Toutefois, vous pouvez supprimer cela en définissant l'événement Listenner à une référence "faible".

Voici comment vous feriez ce que vous feriez: xxx

Le dernier argument définit "useweaklistener" à vrai, ce qui signifie qu'une référence à la chargeur ne sera pas faite. Dans ce cas, le chargeur doit être GC'ed.

L'important à garder à l'esprit est que si vous ajoutez un auditeur d'événement avec une référence forte, vous devez le supprimer (comme vous l'avez fait dans le Exemple). Si vous utilisez un écouteur faible, vous devrez faire une variable privée dans la classe, sinon votre rappel est dans une situation de course avec le GC.

Voici la documentation sur la méthode: http://help.adobe.com/en_us/flashplatform/ Référence / ActionScript / 3 / Flash / Events / IEventDisPatcher.html


5 commentaires

Je ne suis pas sûr que cela soit correct ... Je suis sûr que je comprends des faibles références et ne pensez pas que cela s'applique ici. Réglage usewakreference à true empêche l'enregistrement de l'auditeur d'être utilisé comme comptage de référence pour l'objet / l'instance qui possède l'écoute / rappel. L'objet loader , et sa liste d'écoute doit pouvoir abandonner la portée sans problème, peu importe ce qui se trouve dans la liste des écouteurs ... ne devrait-il pas? Je pense que vous l'avez à l'envers. Ce que je veux savoir, c'est pourquoi loader ne meurt pas ... il serait étrange si chargeur se référencée elle-même pourrait le garder en vie.


... à moins que la liste d'écouteurs ne soit pas conservée sur la cible d'événement ( chargeur dans ce cas) comme je pensais, mais est conservé dans un autre lieu mondial? Cela l'expliquerait.


Selon le code source Flex, il existe un événement mondial qui contient toutes les références dans un lieu centralisé.


Où voyez-vous la référence EventManager? J'ai creusé la source pour une telle référence et je n'ai rien trouvé d'utile. Je viens de rechercher EventManager et je ne peux pas la localiser ... Je viens de voir diverses choses de souris telles que flowelementMouseeventManager . Aussi Cette est la meilleure source en ligne que je puisse créer un lien vers. . Avez-vous une meilleure source?


Éliminer toute confusion pouvant résulter de la lecture de cette réponse (ou de mes commentaires ci-dessus), le L'article relié par NOx est un excellent. Un répartiteur ne peut pas rester en vie parce que l'auditeur est dans le répartiteur, aussi ... IE: Référencer ce que j'ai dit dans mon premier commentaire ici, Loader Se référençant elle-même ( chargeur La méthode d'auditeur) n'est certainement pas suffisante pour garder chargeur vivant.



5
votes

Cet exemple est définitivement sujet d'erreur, puisque le chargeur peut être recruté avant la fin du chargement. Lorsque vous vous abonnez-vous à Terminez Evénement avec ONCOMPLETE Méthode, vous créez une référence à partir de chargeur à votre classe urlladerexample . Et ce dont vous avez besoin pour vous assurer que GC ne ruine pas la chargeuse crée une référence à elle.

GC ne vous garantit jamais de nettoyage rapide, même lorsque vous tuez explicitement toutes les références. ( Voir ce message pour les ressources sur GC Logics. ) mais il peut GARBAGE-Collectez un chargeur dans le processus s'il n'y a pas de références explicites à elle. Si vous essayez votre test dans une application qui utilise la mémoire (et n'est pas simplement assise là-bas sans rien faire), vous êtes susceptible de voir ce comportement. Et vous êtes beaucoup plus susceptible de voir les ordures de chargeur - collectées si vous essayez de charger des SWF au lieu de données.

Utiliser des références faibles n'aident pas ici, car lorsque vous le faites, vous dites à GC: "N'hésitez pas à tuer ce que je, le répartiteur, je fais référence, je n'ai aucune pitié pour cela." Dans votre exemple, ce serait comme suit: "N'hésitez pas à tuer une instance UrlloaderExample si elle perd d'autres références viables", ce qui est, bien, inutile. Voici un bon article sur UseweakReference.

Les auditeurs n'empêchent pas un répartiteur d'être perçus à la poubelle. Un objet inactif est un objet qui n'a plus aucune référence à partir d'autres objets actifs. Donc, si un objet lui-même a des références à quelque chose d'extérieur, il n'empêche pas que cet objet soit retiré de la mémoire.

Donc, pour répondre à votre question brièvement: la référence est nulle part, vous n'avez que de la chance de voir le chargement correctement. Eh bien, pour être complètement précis, c'est l'objet d'activation de la fonction (comme il est appelé dans la spécification ECMA), qui est utilisé comme une portée des variables locales et les références. Mais de toute façon, il est disposé sur le retour de la méthode et vous ne pouvez jamais obtenir une référence à l'objet d'activation lui-même (à nouveau par spécification).

Modifier quelques mots supplémentaires sur qui détient qui tient à la poubelle. Ajouté en raison d'un malentendu apparent dans les commentaires.

Une citation de Adobe LiveDocs :

useweakreference: boolean (par défaut = false) - détermine si la référence à l'auditeur est forte ou faible. Une référence forte (la valeur par défaut) empêche votre auditeur d'être recueillies à la poubelle. Une faible référence ne fait pas.

Ainsi, l'abonnement à l'événement crée une référence du répartiteur à l'auditeur. Et Dispatcher est libre d'aller, contrairement à l'auditeur. Les auditeurs n'empêchent pas un répartiteur d'être recueillis à la poubelle. Et le répartiteur peut empêcher les auditeurs d'être recueillis à la poubelle, c'est pourquoi nous avons usewakreference .


8 commentaires

C'est exactement ce que j'ai pensé était le cas ... loader est toujours autour que parce qu'il n'a pas encore été gc'd. La principale chose me faisant penser qu'il y avait une autre magie était que le Adobe Docs eux-mêmes utilisent cet exemple exact (bien que plus grand). Je suis toujours un peu sceptique à cause d'exemples comme ça!


En outre - qui fait l'URL récupérant le travail après la sortie du constructeur? Est-ce toujours l'instance d'Urlloader de l'Urlloader déréférencée - mais pas encore-gc'd travaillant à l'arrière-plan? Malheureusement, il apparaît que flash.net (où l'urlloader est) est enterré dans le lecteur et n'a pas de Source visible pour cela, donc pas d'aide là-bas. Comme il est à filetage unique, dans les coulisses, je m'attendrais à ce que loader.load ouvre la prise, envoie la requête obtenez , puis définit un rappel avec un Sélectionnez ou quelque chose, mais je devine totalement.


BTW, ce n'est pas tout ce qui est facile à compiler un exemple autonome d'une chargeur étant recueilli dans le processus. Apparemment, le comportement de GC dépend de la charge et des capacités du système et sur le type de lecteur Flash (externe, plugin ou autre). Mais un peu de googling pour ces problèmes vous montrera beaucoup de postes.


@NOX NOCTIS, ER, Quoi? "Cet exemple est définitivement sujettes à l'erreur, puisque le chargeur peut être collecté des ordures avant que le chargement est terminé." Cette déclaration est FALSE . Ce ne serait que GC'ed si l'argument de faim de la fonction AddeventListener est défini sur True. Lorsqu'un auditeur d'événement est ajouté, l'objet que vous attachez actuellement que cela ne reçoive pas la poubelle collectée. Chaque objet contient ses propres références de fonction de gestionnaire, mais si les références sont externes, le GC ne les supprimera pas de la mémoire.


Vous avez tort, j'ai peur. Une citation d'Adobe LiveCs: UseWeakReference: Boolean (par défaut = FALSE) - Détermine si la référence à l'auditeur est forte ou faible. Une référence forte (la valeur par défaut) empêche votre auditeur d'être recueillies à la poubelle. Une référence faible ne signifie pas. Ainsi, l'abonnement à l'événement crée une référence du répartiteur à l'auditeur. Et Dispatcher est libre d'aller, contrairement à l'auditeur. Les auditeurs n'empêchent pas qu'un répartiteur d'être recueilli. Et le répartiteur peut empêcher les auditeurs d'être recueillis à la poubelle.


Remarque: J'accepte cela comme la bonne réponse tel qu'il est, généralement correct pour la question générale. Toutefois, dans l'affaire Urlloader spécifique, il peut y avoir une référence interne / cachée / magique détenue par le joueur qui garde l'instance vivante. Veuillez consulter la réponse et le commentaire de J_A_X.


Je suis avec NOX NOCTIS à ce sujet. La principale chose à comprendre est que le motif est correct si le répartiteur est un objet d'affichage sur scène, car son objet d'affichage parent conservera une référence à celle-ci même si le répartiteur déclaré localement est hors de portée. Dans d'autres cas, comme ci-dessus, lorsque le répartiteur est hors de portée, en fonction de si un événement a été envoyé ou non une des deux choses se produit: 1) Le répartiteur et l'auditeur n'ont aucune référence et sont éligibles de GC ou 2) le répartiteur , événement et auditeur ont tous des références mais sont sur une mémoire de Memory Island et sont éligibles de GC.


Un autre commentaire, à cause du manque d'espace. En outre, malgré la discussion de références "Secret", le code ci-dessus a un bogue, car il ne respecte pas comment Adobe a déclaré que les choses fonctionnent: c'est-à-dire n'importe quel objet qui n'a pas de références ni sur une île de mémoire peut être marquée pour balayer . Si un moment d'exécution particulier survient juste à mettre en œuvre une "référence secrete", alors bien, mais cela ne signifie pas que d'autres actes feront la même chose, il ne rendra pas le code correct, et cela ne signifie pas que la prochaine version de ce runtime fera le même. La méthode correcte consiste à maintenir explicitement une référence au répartiteur.



0
votes

Je ne comprends pas votre confusion. La variable de chargeur fait sort de la portée (comme dans, la référence que vous aviez pointée vers la mémoire n'est plus nécessaire). Cependant, parce qu'il y a toujours une référence à un écouteur d'événement externe, le chargeur lui-même n'est pas GC'ed, c'est pourquoi vous pouvez continuer à faire référence à la référence du chargeur (dans le gestionnaire, il obtient la cible) et ne sera pas éligible Pour GC jusqu'à ce que l'auditeur d'événement soit supprimé.

EDIT: Désolé, j'aurais dû être plus clair sur la question. Je devrais avoir plus de café. Quand je veux dire «externe», je veux dire des choses comme des chargeurs car elles dépendent d'une action de navigateur. Maintenant, je ne peux pas dire cela, car je n'ai jamais vu le code sous-jacent pour Flash Player, mais ma suspicion est que si vous avez des classes de type chargeur qui doit interagir avec le navigateur, il y aura une référence de soulignement ( du joueur au chargeur) qui l'empêchera d'être GC'ed. C'est pourquoi vous devriez Décharger toujours vos chargeurs ou donc, c'est ma compréhension du player Flash. Je pourrais avoir tort, mais j'ai fait plusieurs tests dans le passé et je n'ai jamais vu un auditeur (dans ce cas, mais je me demande s'il y a des autres auditeurs externes qui pourraient arriver avec de l'air) Obtenez GC sans être explicite.


14 commentaires

Veuillez vous reporter à Liens que j'ai fournis ci-dessus . La principale chose à noter est que un objet inactif est celui qui n'a plus aucune référence à partir d'autres objets actifs. Donc, si l'objet lui-même a des références à quelque chose d'extérieur, il n'empêche pas que cet objet soit retiré de la mémoire. L'objet doit avoir des références à rester vivantes.


@J_A_X: Après beaucoup d'études, je suis avec NOX NOCTIS sur celui-ci. Lorsque vous dites "parce qu'il y a toujours une référence à un auditeur d'événements externe", vous devez vous demander (comme je l'ai fait) où cette référence vous parlez est. Ce n'est pas "externe" comme vous le dites. Cette référence est en fait sur chargeur lui-même, et lorsque loader tombe hors de portée, de même que la référence de soi-même. Donc, cela peut être gc'd. Bien que je suis prêt à être convaincu autrement :). Si seulement nous pouvions voir le code source des coulisses sur cette ... :(


@J_A_X Je n'ai jamais impliqué que vous ne savez pas de quoi vous parlez. :) Ce que je disais, c'est que cela fonctionne vraiment de cette façon dans Flash Player, et oui, je l'ai vu de fonctionner de cette manière à plusieurs reprises (bien que je ne puissiez pas la reproduire clairement ce matin sur mon IMAC). Vous déposez toutes les références à un chargeur, vous l'avez au hasard morte. Placons-la de cette façon: je fais juste confiance à ce que les gentils gars comme Grant Skinner disent quand je peux absolument prouver les faits.


@Nox, je ne peux pas dire que j'ai déjà vu ce comportement (ou après un barrage de tests). Personnellement, je ne fais pas de variables de fonction pour quelque chose que vous devez écouter, mais j'étais plus débattant le fait que les chargeurs (et les minuteries apparemment, après avoir parlé à mon ami sur l'équipe SDK) ne reçoivent pas de GC ' Ed parce qu'ils ont une référence dans le lecteur Flash (vous devez charger / démarrer pour ne pas être gc'ed, devez décharger / arrêter pour le GC).


@J_A_X Eh bien, ce serait vraiment génial de savoir quelque chose de l'intérieur. :) J'ai reproduit ce comportement en 2006.2007 2008 et répondu à plusieurs questions sur le sujet à cette époque. C'est pourquoi je suis mort sûr que c'était là (docs officiels et de beaux gars que nous pouvons faire confiance, confirmez également que). Mais les choses changent au fil du temps, peuvent être maintenant nous avons une situation différente. Peut-être que nous ne manquons pas les informations pour la définir.


Ouais, ne serais pas surpris si cela se compose d'une version ultérieure de Flash Player. Ils ont effectué des modifications considérables dans le code de répartition / gc de mémoire sous-jacente.


Toutes bonnes discussions ... résultat final étant que je ne laisserai jamais de telles questions ambiguës d'exister dans mon code. par exemple: dans un cas similaire à mon exemple de code affiché (bien que avec socket au lieu de urlloader ), j'ai explicitement créé une référence à l'instance scopée dentaire en tant que propriété du Classe des parents (mappage à UrlloaderExample dans l'exemple), juste pour assurer une référence claire existante et la "qui tient une référence à cela?" question ne se pose jamais.


@J_A_X @NOX aussi ... Je pense que la réponse finale à ma question est que nous ne savons pas avec certitude. Il se peut que personne ne tient une référence à chargeur et fonctionne uniquement fonctionne car le GC ne le collecte pas assez rapidement. Il peut également être que flash.net est "spécial" et obtenez des références spéciales / magiques dans le lecteur qui le garde en vie. Nous ne pouvons pas dire avec ce dernier parce que ce code n'est pas open source. Est-ce que vous êtes d'accord? Conseil final: faites toujours vos références explicites et ne comptez pas sur des références implicites.


@Russ, non, j'ai parlé aux gens d'Adobe à ce sujet et ils ont tous dit la même chose: "Si c'est un objet ASYNC qui doit parler au lecteur Flash, la référence ne doit pas être cédée jusqu'à décharger". Mais je suis d'accord sur les références explicites.


@J_A_X, je suis malheureusement toujours confondu alors. Qu'est-ce que exactement fait "si c'est un objet async qui doit parler au lecteur flash " signifie? En particulier la dernière partie en italique. Qui tient cette référence et comment identifierait-nous ce que les choses ont besoin de "parler au lecteur Flash". Je peux deviner à beaucoup de choses, mais préférerais certaines informations explicites. Cela semble magique pour le moment. Existe-t-il une sorte de liste d'affichage équivalente où il est assis?


FYI - J'ai posté un commentaire / question sur le Adobe Docs pour Urlloader .


@Russ, Flash Player fournit un ensemble d'API, soit pour des opérations internes ou externes (comme récupération d'informations à partir d'une demande HTTP). Cette demande HTTP n'est pas poignées par Flash Player, elle est gérée par le navigateur. En tant que tel, Toutes les classes avec Flash qui fait référence à une demande externe (tous les chargeurs pour la demande HTTP et la minuterie sont basés sur votre minuterie d'exploitation), le lecteur Flash tiendra une référence «secret» en mémoire , par conséquent, le GC ne le collectera pas. Plus clair?


@J_A_X Sorte de plus claire, et je reçois le gist, sauf que les frontières sont toujours floues. Par exemple: Si une minuterie d'OS compte comme une référence externe, alors les API graphiques du système d'exploitation et toutes les E / S. On dirait que cela couvrirait à peu près tous les types d'instances. Peut-être que s'ils n'avaient pas d'opérations en attente (comme une prise ou une demande HTTP), vous pourriez faire valoir qu'il n'y aurait aucune raison de tenir une référence secrète, mais nous faisons ensuite des hypothèses sur la mise en œuvre. Comme je le disais, je vais éviter de savourer un vaudou et garder des références explicites! Être pas clair sur la façon dont quelque chose fonctionne est une mauvaise pratique.


@Russ, OS Graphics sont "écriture seulement", I / O est basé sur l'événement Flash Player à écouter du système d'exploitation, puis expédiant en interne. D'après ce que j'ai dit, seuls les chargeurs et les minuteries ont une référence secrète en raison de la façon dont il fonctionne avec le système d'exploitation. Je suis d'accord sur les références explicites, vous devriez toujours faire cela, mais il est bon de savoir la raison sous-jacente pour la raison pour laquelle certains de ces objets ne peuvent pas gc :)



1
votes

Eh bien-être, si votre question était la raison pour laquelle le chargeur est vivant hors de portée, voici votre réponse:

En fait, le chargeur que vous voyez dans la méthode du constructeur de classe (CCM) n'est pas la même chose que vous voyez dans OnComplete : LINE14: var chargeur: URLLOADER = URLLOADER (EVT.TARGET);

(Je ne sais pas pourquoi les gens aiment-ils mettre le même nom aux variables, qui peuvent être déroutants, mais Ce n'est pas dans le cas que je vais expliquer)

la magie réside dans le evt.target . Mais vous devriez vous demander: "Qu'est-ce que le .Target fait?" Eh bien c'est une variable d'instance définie par "l'objet d'événement" et fournit une référence à "l'objet cible".

Si vous ne savez pas ce qu'est un "objectif d'événement", lisez ce paragraphe. La "cible d'événement" est l'objet dans lequel l'écouteur enregistré à ligne9: loader.addeventlistener (Event.clete, OnComplete); Comme vous pouvez le constater, c'est le chargeur au CCM (veuillez ne pas confondre Les noms de chargeur) qui fait référence à nouvel Urlloader (); . Paragraphe Conclusion: "Cible d'événement" est l'objet Urlloader référencé par la variable locale de charge au CCM.

Eh bien, en utilisant la variable .Target, vous pouvez avoir une référence à l'objet Urlloader, puis utilisez la référence que vous le souhaitez. Dans le cas, vous avez utilisé la référence pour supprimer l'auditeur. Ça va bien.

Voici une version améliorée (une seule variable d'instance qui fournit des références hachiy à Urlloader): xxx

mais juste pour faire du shure Arent confondu avec les noms: xxx

acclamations ... Si vous avez des doutes à l'opération de coulée utilisée à urlloader (evt.target); vous pouvez demander.


0 commentaires