Java est un type de langue de collecte des ordures. Son avantage est que les développeurs n'ont pas besoin de gérer délibérément l'allocation de la mémoire, ce qui réduit la possibilité que les applications se bloquent en raison des défauts de segmentation locale et empêche la mémoire de dégelation de presser la pile (tas). Par conséquent, le code écrit est plus sûr.
Malheureusement, il y a encore de nombreuses fuites logiques en Java qui sont sujettes aux fuites de la mémoire. Si vous ne faites pas attention, votre application Android peut facilement gaspiller la mémoire non inférieure, ce qui entraînera éventuellement un jet d'erreur de mémoire (hors de la mémoire, OOM).
1. La raison de la fuite de mémoire générale est que lorsque toutes les références à l'objet ont été publiées, l'objet n'a pas été libéré. (Note du traducteur: le curseur a oublié de fermer, etc.)
2. La raison de la fuite de mémoire logique est que lorsque l'application n'a plus besoin de cet objet, toutes les références à l'objet n'ont pas été publiées.
Si vous tenez une forte référence à un objet, le collecteur des ordures ne peut pas recycler l'objet en mémoire.
Dans le développement d'Android, le problème de fuite de mémoire le plus probable est le contexte. Par exemple, le contexte de l'activité contient un grand nombre de références de mémoire, telles que les hiérarchies de vue et autres ressources. Une fois qu'un contexte est divulgué, cela signifie également la fuite de tous les objets qu'il pointe. Les machines Android ont une mémoire limitée et trop de fuites de mémoire peuvent facilement conduire à l'OOM.
La détection des fuites de mémoire logique nécessite un jugement subjectif, en particulier le cycle de vie de l'objet n'est pas clair. Heureusement, l'activité a un cycle de vie clair et il est facile de trouver la cause de la fuite. Activité.ondestroy () est considéré comme la fin de la vie de l'activité. Programmatiquement, il doit être détruit, ou le système Android doit recycler cette mémoire (note du traducteur: lorsque la mémoire est insuffisante, Android recyclera les activités invisibles).
Si cette méthode est exécutée, il y a toujours une forte référence à l'activité dans la pile, et le collecteur des ordures ne peut pas le marquer comme une mémoire recyclée, et notre objectif d'origine est de le recycler!
Le résultat est que l'activité survit en dehors de son cycle de vie.
L'activité est un objet poids lourd et doit être géré par le système Android. Cependant, les fuites de mémoire logique se produisent toujours par inadvertance. (Note du traducteur: j'ai une fois essayé une activité qui a provoqué une fuite de mémoire de 20 m). Dans Android, il n'y a que deux pièges qui mènent à des fuites de mémoire potentielles:
La variable statique du processus global (processus-global). Ce monstre qui ignore l'état de l'application et détient une forte référence à l'activité.
Les fils qui vivent en dehors du cycle de vie de l'activité. Aucune référence forte à l'activité n'a été éliminée.
Vérifiez si vous avez rencontré les situations suivantes.
1. Activités statiques
La variable d'activité statique est définie dans la classe et l'instance d'activité en cours d'exécution est affectée à cette variable statique.
Si cette variable statique n'est pas effacée après la fin du cycle de vie de l'activité, elle entraînera une fuite de mémoire. Étant donné que les variables statiques traversent le cycle de vie de cette application, les activités divulguées existeront toujours dans le processus d'application et ne seront pas collectées par le collecteur des ordures.
activité d'activité statique; void setStaticActivity () {activité = this;} View sabutton = findViewByid (r.id.sa_button); sabutton.setOnClickListener (new View.OnClickListener () {@Override public void onClick (View v) {setStaticActivity (); NextActivity ();}});2. vues statiques
Des situations similaires peuvent se produire en mode singleton, et si l'activité est souvent utilisée, il est pratique de sauvegarder une instance en mémoire. Comme mentionné précédemment, forcer le cycle de vie d'une activité est assez dangereux et inutile, et cela ne peut pas être fait de toute façon.
Cas spécial: Si une initialisation de la vue consomme beaucoup de ressources et reste inchangée pendant un cycle de vie de l'activité, elle peut être transformée en statique et chargée sur la hiérarchie de vue. De cette façon, lorsque l'activité est détruite, la ressource doit être libérée. (Note du traducteur: la mémoire n'est pas libérée dans l'exemple de code.
vue statique; void SetStaticView () {View = findViewById (R.Id.Sv_Button);} View SvButton = FindViewByid (R.ID.Sv_Button); SvButton.SetOnClickListener (New View.OnClickListener () {@Override Public Void OnClick (View V) {SetStaticView (); NextActivity (););3. Classes approfondies
Continuez, en supposant qu'il existe une classe interne dans l'activité, cela peut améliorer la lisibilité et l'encapsulation. Si nous créons une classe interne et tenons une référence à une variable statique, félicitations, la fuite de mémoire n'est pas loin de vous (Note du traducteur: vide lors de la détruire, euh).
objet statique privé intérieur; void CreateInnerClass () {class InnerClass {} inner = new InnerClass ();} View iCButton = findViewByid (R.Id.ic_Button); icbutton.SetOnClickListener (new View.OnClickListener () {@Override public Void OnClick (View v) {CreateInnerClass (););L'un des avantages des classes internes est qu'ils peuvent accéder aux classes externes. Malheureusement, la raison des fuites de mémoire est que les classes internes contiennent de fortes références aux instances de classe externe.
4. Cours anonymes
De même, les classes anonymes maintiennent également des références aux classes externes. Les fuites de mémoire sont donc faciles à se produire lorsque vous définissez Anonymous AsyncTSK dans votre activité. Lorsque la tâche asynchrone exécute une tâche longue en arrière-plan, l'activité est malheureusement détruite (Note du traducteur: sorties de l'utilisateur, recyclage du système), cette instance d'activité détenue par AsyncTask ne sera pas recyclée par le collecteur des ordures jusqu'à ce que la tâche asynchrone soit terminée.
void startaSyncTask () {new AsyncTask <void, void, void> () {@Override Protected void doinbackground (void ... params) {while (true); }} .execute ();} super.oncreate (SavedInstanceState); setContentView (r.layout.activity_main); voir AicButton = findViewByid (r.id.at_button); AicButton.setonclickListener (new View.OnClickListener () {@Override public Void on onClick (View V) {startasnener (); NextActivity ();}});5.Handleur
De la même manière, définissez l'anonymous coulable et exécutez-le avec un gestionnaire de classe anonyme. La classe intérieure Runnable tiendra une référence implicite à la classe externe et sera transmise à la file d'attente de messages MessageQueue de la file d'attente du gestionnaire. L'instance d'activité ne sera pas détruite tant que le message du message ne sera pas traité, entraînant une fuite de mémoire.
void CreateHandler () {new Handler () {@Override public void handleMessage (message message) {super.HandleMessage (message); }} .Postdelayed (new Runnable () {@Override public void run () {while (true);}}, long.max_value >> 1);} CreateHandler ();6.
Nous montrons à nouveau des fuites de mémoire à travers le fil et la tricotage.
void spawnthread () {new Thread () {@Override public void run () {while (true); }} .start ();} View tbutton = findViewByid (r.id.t_button); tbutton.setOnclickListener (new View.OnClickListener () {@Override public void onClick (View V) {SpawnThread (); NextActivity ();}});7.Timertask
Tant qu'il s'agit d'une instance d'une classe anonyme, qu'elle soit ou non dans le thread de travailleur, il tiendra une référence à l'activité, entraînant une fuite de mémoire.
void scheduleTimer () {new Timer (). Schedule (new TimemerSask () {@Override public void run () {while (true);}}, long.max_value >> 1);} voir ttbutton = finwViewByid (r.id.tt_button); ttbutton.setonclickListener (new View.onclick); ttbutton.setonclickListener (new View.onclick); ttbutton.setonClickListener (New View.onclick);) void onClick (View v) {scheduleTimer ();8. Sensor Manager
Enfin, les services système peuvent être obtenus via context.getSystemService (int name). Ces services fonctionnent dans leurs processus respectifs, aidant les applications à gérer les tâches de fond et les interactions matérielles. Si vous avez besoin d'utiliser ces services, vous pouvez enregistrer les auditeurs, ce qui entraînera une référence au contexte. Si ces auditeurs ne sont pas déconnectés lorsque l'activité est détruite, elle entraînera des fuites de mémoire.
void registerListener () {SensorManager sensorManager = (sensorManager) getSystemService (Sensor_Service); Capteur capteur = sensorManager.getDefaultSensor (capteur.type_all); SensorManager.RegisterListener (This, Sensor, sensorManager.Sensor_delay_Fastest);} View Smbutton = FindViewByid (R.ID.SM_BUTTON); SMBUTTON.SetOnClickListener (New View.OnClickListener () {@Override Public Void OnClick (View v) {RegisterListener ();););Résumer
Après avoir vu tant d'exemples qui peuvent conduire à des fuites de mémoire, il est facile de manger toute la mémoire de votre téléphone, de la collecte et du traitement des ordures plus fréquemment, et même dans le pire des cas, cela mènera à l'OOM. Les opérations de collecte des ordures sont coûteuses et peuvent conduire à des retards visibles. Par conséquent, faites attention à la chaîne de référence tenue lors de l'instanciation et effectuez souvent des chèques de fuite de mémoire.