[Le code source de cet article est disponible en téléchargement : Les modules.zip

Mais avant de nous attaquer au problème voici une petite définition des modules, bien entendu pour ceux qui la connaissent vous pouvez passer directement au paragraphe suivant.

Les modules

Un module représente une fonctionnalité ou un ensemble de fonctionnalités regroupées de manière logique et pouvant être partagé par plusieurs applications où propre à une seule si par exemple on décide de diviser la charge de travail en équipe. Le module n'est pas autonome, il a besoin d'un environnement d'exécution: ici d'une application flex.

Dans flex les modules se présentent sous la forme d'un composant MXML qui se charge et se décharge dynamiquement par l'application principale que nous appellerons pour le reste de cet article hôte.

Les modules ne se limitent pas uniquement à du visuel, ils peuvent être aussi un ensemble de classes et de fonctions : une librairie externe en quelque sorte.

Les modules dans Flex

D'un point de vue du code, les modules sont des composants MXML qui héritent du composant Module où une classe héritant de BaseModule dans le cas d'une libraire dynamique contenant des classes et/ou fonctions.

Le domaine d'application

Nous savons qu'un module est un swf externe que l'on charge dynamiquement, ce swf possède ce qu'on appelle un domaine d'application dans lequel se trouve ses définitions de classes. Ce domaine permet de définir le niveau de sécurité de son contenu pour un éventuel partage entre applications. Il existe un document qui en parle très bien Utilisation de la classe ApplicationDomain

Le rôle de SystemManager dans les modules

Le SystemManager est le chef-orchestre d'une application : le lanceur applicatif, celui qui contient et affiche l'application pour être plus précis. Il ne fait pas que ça bien entendu, c'est lui par exemple qui s'occupe d'affiche les popups, les tooltips et les curseurs permettant à ces derniers d'être toujours au dessus de votre application... mais surtout c'est lui le responsable du problème de fuite de mémoires avec les modules.

Regardons cela de plus prêt à travers une application hôte qui charge deux modules et nous constaterons les fuites de mémoire grâce au profileur.

Voici la structure de notre application :

structure application modulaire

Maintenant observons la du point de vue des SystemManager :

Les modules et les systemManagers

Nous remarquons que les systemManagers des modules chargés sont référencés dans celui de l'application hôte. Ces références sont automatiquement détruites après leurs déchargement SAUF qu'il reste toujours en mémoire au moins une instance d'un module.

Pour le vérifier lançons l'application en mode profilage et sélectionnons uniquement les options pour observer la mémoire :

Photo_1.png

Maintenant ajoutons plusieurs contacts à notre application et retournons dans le profileur.

Photo_2.png Photo_3.png Photo_4.png

Pour observer le bon fonctionnement du garbage collector, nous allons effectuer un cliché mémoire. Le cliché mémoire est la représentation mémoire des objets présents à un instant T. Lorsque nous effectuons ce cliché, le profileur déclenche en même temps le passage du garbage collector, nous permettant de vérifier son bon fonctionnement en détruisant les objets.

Photo_5.png

Nous pouvons donc imaginer le pire lorsque votre application utilise des modules plus complexes que ceux que nous vous proposons. Nous avons pu observer sur le terrain de nombreuses applications industrielles montées en charge pouvant atteindre un plafond mémoire impensable, le premier réflexe est de penser que les modules sont mal conçus et de les écarter du développement.

La solution au problème de fuites mémoires

Si nous observons le code source de la classe SystemManager à la ligne 208 :

/**
	 *  @private
	 *  The last SystemManager instance loaded as child app domains
	 */
	mx_internal static var lastSystemManager:SystemManager;

et bien, le systemManager de l'hôte conserve la référence du systemManager du dernier module chargé, hey hey ;)

La solution est simplement de marquer cette référence à null pour qu'elle soit bien détruite par le garbage collector. Nous devons le faire lorsque le module est déchargé en écoutant l'événement unload sur ModuleLoader.

Ajoutons le code suivant à notre application hôte :

<mx:ModuleLoader id="loader" url="FormModule.swf" ready="onReady()" unload="onUnload()" />
 
	<mx:Script>
		<![CDATA[
			...
			import mx.core.mx_internal;
 
			use namespace mx_internal;
			...
			private function onUnload():void {
				SystemManager.lastSystemManager = null;
			}
		]]>
	</mx:Script>

Testons le code à présent en ajoutant des contacts, en affichant plusieurs fois les modules pour effectuer une montée en charge et prenons ensuite un cliché de mémoire et là : magique, IL NE RESTE PLUS QUE LE MODULE AFFICHE EN MEMOIRE !

Image_1.png Image_2.png

Il y a bien entendu d'autres facteurs en prendre en compte concernant la gestion mémoire dont vous pouvez trouver les resources sur le blog de Grant Skinner :

Resource management pt 1
Resource management pt 2
Resource management pt 3
Weakly Referenced Listeners

Conclusion

Le rôle des experts est de dénicher ce genre d'information qui peut parfois faire basculer le choix que doivent prendre les entreprises sur une technologie telle que Flex. Il est dommage que les modules soient parfois la cause de l'abandon de l'utilisation du framework Flex pour une autre technologie.

Compléments d'informations

Nous avons oublié de faire mention dans l'article qu'il arrive parfois que l'événement unload ne soit pas diffusé. Il suffit pour cela d'utiliser d'autres mécanismes pour marquer la référence SystemManager. lastSystemManager à null.