Android - inventaire des fuites de mémoire courantes

1. La nature des fuites de mémoire

L'essence des fuites de mémoire est que les références d'objet ne sont pas libérées . Lorsque des objets sont créés, s'ils ne sont pas libérés correctement, ces objets occuperont toujours de la mémoire jusqu'à la fermeture de l'application. Par exemple, lorsqu'une activité est détruite, si elle contient toujours des références à d'autres objets, ces objets ne peuvent pas être récupérés par le ramasse-miettes, ce qui entraîne des fuites de mémoire.

Lorsqu'il y a une fuite de mémoire, nous devons utiliser GCRoot pour identifier les objets et les références de la fuite de mémoire.

GCRoot est le nœud racine du mécanisme de récupération de place. Le nœud racine comprend des piles de machines virtuelles, des piles de méthodes locales, des références d'attributs statiques de classe dans la zone de méthode, des threads actifs, etc. Ces objets sont considérés comme des "objets vivants" par la récupération de place. mécanisme et ne sera pas recyclé.

Lorsque le mécanisme de récupération de place est exécuté, il démarre à partir de GCRoot, parcourt toutes les références d'objets et marque tous les objets vivants. Les objets non marqués sont des objets poubelles et seront recyclés.

Lorsqu'il y a une fuite de mémoire, le mécanisme de récupération de place ne peut pas récupérer certains objets qui ne sont plus utilisés, et ces objets sont toujours référencés, formant une chaîne de référence de certains objets GCRoot vers des objets ayant fui de la mémoire, ces objets ne seront pas recyclés, ce qui entraînera de la mémoire fuites.

En trouvant la chaîne de référence entre l'objet de fuite de mémoire et GCRoot, vous pouvez localiser la source de la fuite de mémoire, puis résoudre le problème de fuite de mémoire. LeakCancry est implémenté via ce mécanisme.

Certains GCRoots courants incluent :

  • L'objet référencé dans la pile de la machine virtuelle (variable locale).

  • L'objet référencé par la propriété statique (variable statique) dans la zone de méthode.

  • Objet référencé par JNI.

  • L'objet référencé par un thread Java (Thread).

  • Un objet détenu par un verrou synchronisé en Java.

Qu'est-ce qui empêchera la référence d'objet d'être publiée ? Juste quelques exemples :

  • Fuites de mémoire causées par des classes internes anonymes : les classes internes anonymes contiennent généralement des références à des classes externes. Si le cycle de vie de la classe externe est plus long que celui de la classe interne anonyme, (correction, il n'est pas approprié d'utiliser le cycle de vie ici, lorsque la classe externe est détruite, la classe interne ne sera pas automatiquement détruite, car la classe interne n'est pas une variable membre de la classe externe, ce ne sont que des objets créés dans le cadre de la classe externe, donc le moment de la destruction de l'interne est différente de celle de la classe externe, elle ne dépendra donc pas de l'existence ou non d'une référence détenue à l'objet correspondant), la classe externe ne pourra pas être recyclée, ce qui entraînera une fuite de mémoire.

  • La variable statique contient une référence à l'activité ou au contexte : si une variable statique contient une référence à l'activité ou au contexte, ces activités ou ce contexte ne peuvent pas être récupérés par le ramasse-miettes, ce qui entraîne une fuite de mémoire.

  • Objets Cursor, Stream ou Bitmap non fermés : si le programme ne ferme pas correctement ces objets lors de l'utilisation d'objets Cursor, Stream ou Bitmap, ces objets occuperont toujours de la mémoire, ce qui entraînera des fuites de mémoire.

  • Ressources non publiées : si le programme ne libère pas correctement ces ressources lors de l'utilisation des ressources système, comme ne pas fermer les connexions à la base de données, ne pas libérer les ressources audio, etc., ces ressources occuperont toujours de la mémoire, ce qui entraînera des fuites de mémoire.

2. Fuites de mémoire causées par des références statiques

Lorsqu'un objet est détenu par une variable statique, même si l'objet n'est plus utilisé, il ne sera pas récupéré par le ramasse-miettes, ce qui provoquera une fuite mémoire

public class MySingleton {
    private static MySingleton instance;
    private Context context;

    private MySingleton(Context context) {
        this.context = context;
    }

    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }
}

Dans le code ci-dessus, MySingleton contient une référence à un objet Context, et MySingleton est une variable statique, donc même si cet objet n'est plus utilisé, il ne sera pas recyclé par le garbage collector.

Si vous devez utiliser des variables statiques, veillez à les définir sur null lorsque vous n'en avez pas besoin, afin que la mémoire puisse être libérée à temps.

3. Fuites de mémoire causées par des classes internes anonymes

La classe interne anonyme contient implicitement une référence à la classe externe. Si la classe interne anonyme est conservée, la classe externe ne peut pas être ramassée.

public class MyActivity extends Activity {
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        button = new Button(this);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // do something
            }
        });
        setContentView(button);
    }
}

La classe interne anonyme OnClickListener contient une référence à la classe externe MyActivity. Si le bouton n'est pas effacé avant la destruction de MyActivity, MyActivity ne peut pas être ramassé. (Le bouton peut être considéré ici comme un objet défini par soi-même, et la solution générale consiste à définir l'objet bouton sur vide)

Lorsque l'activité est détruite, tous les objets contenant des références d'activité doivent être définis sur null.

4. Fuite de mémoire causée par Handler

Handler est un mécanisme de communication de thread couramment utilisé dans les applications Android. Si Handler est utilisé de manière incorrecte, cela entraînera des fuites de mémoire.

public class MyActivity extends Activity {
    private static final int MSG_WHAT = 1;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_WHAT:
                    // do something
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendEmptyMessageDelayed(MSG_WHAT, 1000 * 60 * 5);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 在Activity销毁时,应该将Handler的消息队列清空,以避免内存泄漏。
        mHandler.removeCallbacksAndMessages(null);
        }
}

Le gestionnaire contient une référence à l'activité. S'il y a des messages non traités dans la file d'attente des messages du gestionnaire avant que l'activité ne soit détruite, l'activité ne peut pas être ramassée.

Lorsque l'activité est détruite, la file d'attente des messages du gestionnaire doit être vidée pour éviter les fuites de mémoire.

5. Fuite de mémoire causée par un objet Bitmap

Lorsqu'un objet Bitmap est créé, il occupera une grande quantité de mémoire, et s'il n'est pas libéré à temps, il provoquera une fuite de mémoire.

public class MyActivity extends Activity {
    private Bitmap mBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 加载一张大图
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_image);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 释放Bitmap对象
        mBitmap.recycle();
        mBitmap = null;
    }
}

Lorsque l'activité est détruite, l'objet Bitmap mBitmap doit être libéré à temps, sinon cela provoquera une fuite de mémoire.

Lors de l'utilisation d'un grand nombre d'objets Bitmap, les objets inutilisés doivent être recyclés à temps pour éviter les fuites de mémoire. De plus, vous pouvez envisager d'utiliser des bibliothèques de chargement d'images pour gérer les objets Bitmap, tels que Glide, Picasso, etc.

6. Fuites de mémoire causées par des ressources non fermées

Lorsque certaines ressources système sont utilisées, telles que des fichiers, des bases de données, etc., si elles ne sont pas fermées à temps, des fuites de mémoire peuvent se produire. Par exemple:

public void readFile(String filePath) throws IOException {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(filePath);
        // 读取文件...
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Dans le code ci-dessus, si l'objet FileInputStream n'est pas fermé à temps après la lecture du fichier, cela peut provoquer une fuite de mémoire.

Lorsque vous utilisez certaines ressources système, telles que des fichiers, des bases de données, etc., fermez les objets associés à temps pour éviter les fuites de mémoire.

Pour éviter les fuites de mémoire, vous devez faire attention lorsque vous écrivez du code, nettoyer les objets inutilisés à temps et vous assurer que les ressources mémoire sont libérées à temps. Dans le même temps, certains outils peuvent être utilisés pour détecter les fuites de mémoire, comme Android Profiler, LeakCanary, etc.

7. Fuite de mémoire WebView

public class MyActivity extends Activity {
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = findViewById(R.id.webview);
        mWebView.loadUrl("https://www.example.com");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 释放WebView对象
        if (mWebView != null) {
            mWebView.stopLoading();
            mWebView.clearHistory();
            mWebView.clearCache(true);
            mWebView.loadUrl("about:blank");
            mWebView.onPause();
            mWebView.removeAllViews();
            mWebView.destroy();
            mWebView = null;
        }
    }
}

Lorsque l'activité est détruite, l'objet WebView doit être libéré à temps, sinon cela peut provoquer une fuite de mémoire.

Lorsque vous utilisez WebView, pour libérer l'objet WebView à temps, vous pouvez appeler la méthode destroy de WebView lorsque l'activité est détruite, et également effacer l'historique et le cache de WebView pour vous assurer que toutes les ressources sont libérées.

8. Outils de suivi

  1. Outil de surveillance de la mémoire : Android Studio fournit un outil de surveillance de la mémoire, qui peut surveiller l'utilisation de la mémoire de l'application en temps réel pendant le processus de développement, aidant les développeurs à trouver les fuites de mémoire à temps.

  2. DDMS : l'outil DDMS du SDK Android peut surveiller les processus et les threads des appareils ou émulateurs Android, y compris des informations telles que l'utilisation de la mémoire et les traces de la pile, et peut être utilisé pour diagnostiquer les fuites de mémoire.

  3. MAT : MAT (Memory Analyzer Tool) est un outil d'analyse de la mémoire basé sur Eclipse qui peut analyser l'utilisation de la mémoire du tas de l'application, identifier et localiser les fuites de mémoire.

  4. Tencent's Matrix est aussi un très bon projet open source, je recommande à tous de l'utiliser

Une fuite de mémoire signifie que certains objets ou ressources du programme ne sont pas correctement libérés, ce qui entraîne une augmentation de l'utilisation de la mémoire, ce qui peut éventuellement entraîner des problèmes tels que des pannes d'application ou un ralentissement du système.

Les problèmes courants de fuite de mémoire et les meilleures pratiques correspondantes sont organisés comme suit

 

Guess you like

Origin blog.csdn.net/m0_59482482/article/details/132054435