Un avenir planifié peut-il provoquer une fuite de mémoire?

Je pense que j'ai une fuite de mémoire dans mon fond d'écran en direct Android. Chaque fois que je tourne l'écran, la quantité de mémoire collectée augmente de 50kb et ne recule pas. Je pense que cela peut être causé par un avenir planifié, alors je vais présenter un scénario pour voir si c'est le cas.

Disons que vous avez une classe (appelons-le Foo) qui a les membres suivants.

  • Xamarin.Forms SetHasNavigationBar false provoque un saut sur PushAsync
  • Exception Java.lang.Runtime: la photo a-t-elle échoué?
  • Différence entre les API Google cible et la cible Android
  • Pourquoi la vue est-elle glissée avec ViewDragHelper réinitialisé à sa position d'origine sur la mise en page ()?
  • Android - Créer des SMS à partir de l'API PDU obsolète?
  • Gérer les actifs entre le bureau et la version de l'appareil dans libgdx
  • private ScheduledFuture<?> future; private final ScheduledExecutorService scheduler = Executors .newSingleThreadScheduledExecutor(); private final Runnable runnable = new Runnable() { public void run() { // Do stuff } }; 

    Et maintenant, vous définissez un avenir planifié

     future = scheduler.scheduleAtFixedRate(runnable, delay, speed, TimeUnit.MILLISECONDS); 

    L'avenir contient une référence à l'exécution, et le runnable contient une référence à l'objet Foo principal. Je ne sais pas si c'est le cas, mais cela pourrait-il signifier que si rien dans le programme ne fait référence à Foo, le collecteur d'ordures ne peut toujours pas le collecter car il y a un avenir planifié? Je ne suis pas trop bon en multithreading, donc je ne sais pas si le code que j'ai montré signifie que la tâche programmée sera plus longue que l'objet, ce qui signifie qu'il ne finira pas par être récupéré.

    Si ce scénario n'empêchera pas Foo d'être la collecte des ordures, il me faut simplement le dire avec une explication simple. Si cela empêche Foo d'être récupéré, alors, comment le réparer? Doit faire future.cancel(true); future = null; future.cancel(true); future = null; ? Le future = null partie future = null n'est-il pas nécessaire?

  • Arrêtez Android Studio à l'aide de la bibliothèque d'assistance
  • Comment gérer les boutons "enregistrer" et "annuler" et la touche arrière
  • Combinaison d'une image bitmap (côte à côte)
  • Appcompat-v7: 21.0.0 ': Aucune ressource trouvée qui correspond au prénom: attr' android: actionModeShareDrawable '
  • Comment imprimer à l'aide d'une imprimante zébrée dans Android?
  • Liaison de données avec des auditeurs personnalisés sur une vue personnalisée
  • 3 Solutions collect form web for “Un avenir planifié peut-il provoquer une fuite de mémoire?”

    • Soit votre méthode de run repose sur la classe Foo jointe et ne peut donc pas vivre de manière indépendante. Dans ce cas, je ne vois pas comment vous pourriez avoir votre Foo gc'ed et garder votre exécution "vivant" pour être géré par l'exécuteur testamentaire
    • Ou votre méthode d' run est statique dans le sens où elle ne dépend pas de l'état de votre classe Foo , auquel cas vous pourriez la rendre statique et cela empêchera le problème que vous rencontrez.

    Vous ne parvenez pas à gérer l'interruption dans votre Runnable. Cela signifie que même si vous appelez future.cancel(true) votre Runnable continuera à courir, ce qui, comme vous l'avez déterminé, pourrait être une cause de votre fuite.

    Il existe plusieurs façons de rendre un Runnable "intermittent". Vous appelez une méthode qui lance InterruptedException (comme Thread.sleep() ou une méthode d' Thread.sleep() ) et elle lancera une InterruptedException lorsque l'avenir sera annulé. Vous pouvez attraper cette exception et quitter la méthode d'exécution rapidement après avoir nettoyé ce qu'il faut nettoyer et restaurer l'état interrompu:

     public void run() { while(true) { try { someOperationThatCanBeInterrupted(); } catch (InterruptedException e) { cleanup(); //close files, network connections etc. Thread.currentThread().interrupt(); //restore interrupted status } } } 

    Si vous n'appeliez pas à ces méthodes, l'idiome standard est:

     public void run() { while(!Thread.currentThread().isInterrupted()) { doYourStuff(); } cleanup(); } 

    Dans ce cas, vous devriez vous assurer que la condition dans le temps est vérifiée régulièrement.

    Avec ces changements, lorsque vous appelez future.cancel(true) , un signal d'interruption sera envoyé au thread exécutant votre Runnable qui va quitter ce qu'il fait, ce qui rend votre Runnable et votre instance Foo éligibles pour GC.

    Bien que cette question soit répondu depuis longtemps mais après avoir lu cet article, j'ai pensé publier une nouvelle réponse avec explication.

    Un avenir planifié peut-il provoquer une fuite de mémoire? — OUI

    ScheduledFuture.cancel() ou Future.cancel() en général ne notifie pas à son Executor qu'il a été annulé et qu'il reste dans la file d'attente jusqu'à ce qu'il arrive son exécution. Ce n'est pas une affaire importante pour les Futures simples mais peut être un gros problème pour ScheduledFutures . Il peut y rester pendant des secondes, des minutes, des heures, des jours, des semaines, des années ou presque indéfiniment en fonction des retards prévus.

    Voici un exemple avec le pire scénario. Le runnable et tout ce qui se réfère restera dans les millisecondes Queue for Long.MAX_VALUE, même après que Future ait été annulée!

     public static void main(String[] args) { ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); Runnable task = new Runnable() { @Override public void run() { System.out.println("Hello World!"); } }; ScheduledFuture future = executor.schedule(task, Long.MAX_VALUE, TimeUnit.MILLISECONDS); future.cancel(true); } 

    Vous pouvez le voir en utilisant un Profiler ou en appelant la méthode ScheduledThreadPoolExecutor.shutdownNow() qui renverra une liste avec un élément (c'est le Runnable qui a été annulé).

    La solution à ce problème est soit d'écrire votre propre implémentation future, soit d'appeler la méthode purge() temps en temps. Dans le cas de la solution d'usine d'Executor personnalisée, vous trouverez:

     public static ScheduledThreadPoolExecutor createSingleScheduledExecutor() { final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); Runnable task = new Runnable() { @Override public void run() { executor.purge(); } }; executor.scheduleWithFixedDelay(task, 30L, 30L, TimeUnit.SECONDS); return executor; } 

    L'avenir contient une référence à l'exécution, et le runnable contient une référence à l'objet Foo principal. Je ne sais pas si c'est le cas, mais cela pourrait-il signifier que si rien dans le programme ne fait référence à Foo, le collecteur d'ordures ne peut toujours pas le collecter car il y a un avenir planifié?

    C'est une mauvaise idée de faire de Foo une sorte d'objet transitoire que vous créez souvent parce que vous devez arrêter le ScheduledExecutorService scheduler lorsque votre application se ferme. Ainsi, vous devriez faire de Foo un pseudo-singleton.

    Le collecteur d'ordures comprend des cycles donc une fois que vous Foo le service d'exécuteur de Foo , vous n'aurez probablement pas de problèmes de mémoire.

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.