内存泄漏示例(解释)

2026-03-04  本文已影响0人  雨来
package com.ity.tyvoddemo.activity;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.WindowManager;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.ity.tyvoddemo.R;
/**
 * author : wenPengHu
 * date : 2026/3/5 14:14
 * description :内存泄漏,java中的可达性分析算法,可达则不可回收 从GCRoot到某个变量或对象可达,则这个变量或对象则不能被回收,
 * 如果一个变量或对象从GCRoot不可达,则可以被GC回收;
 * 什么是内存泄漏,一个对象不应该被使用了,结果还在内存中这就是内存泄漏,泄漏产生的根本原因是:短生命周期的对象被长生命周期对象引用(我中有你:说明你被我引用了)(比如内部类持有外部类的 这里讲的
 * 是什么是持有,而非长短生命周期)
 */
//public class MemoryLeakAct extends AppCompatActivity {
//    private static final String TAG = "MemoryLeakAct";
//    @Override
//    protected void onCreate(Bundle savedInstanceState) {
//        super.onCreate(savedInstanceState);
//        EdgeToEdge.enable(this);
//        setContentView(R.layout.activity_memory_leak);
//        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
//            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
//            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
//            return insets;
//        });
//        //Handler持有runnable(Handler通过Message持有Runnable 而Runnable持有 Activity) 而这个Handler又是主线程的Handler它是一个GcRoot
//        /**
//         * 这个Handler会一直存在内存中
//         * 1、打开这个Activity后立马关闭,因为Activity被Runnable持有所以 Activity会泄漏
//         * 2、如果60秒后刚好有Gc过来 Activity泄漏的内存会被回收的
//         * 3、如果进入这个页面等待60秒后关闭这个Activity 则不会存在内存泄漏,因为Runnable这条链条被破坏掉了
//         */
//        new Handler(Looper.myLooper()==Looper.getMainLooper()?Looper.myLooper():Looper.getMainLooper()).postDelayed(new Runnable() {
//            @Override
//            public void run() {
//                Context context = MemoryLeakAct.this;
//                //进程内只有一个 windowManager 实例
//                WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//                int width =  windowManager.getDefaultDisplay().getWidth();
//                Log.d(TAG, "run: screen_width = " +width);
//            }
//        },60_000);
//
//    }
//}



/**
 * author : wenPengHu
 * date : 2026/3/5 14:14
 * description :
 * 内存泄漏与可达性分析:
 * 1. 可达性分析:从GCRoot出发,能到达的对象不可回收,不能到达的对象可回收
 * 2. 内存泄漏:本该被回收的对象,因为还被GCRoot引用着,无法回收
 * 3. 根本原因:短生命周期对象(如Activity)被长生命周期对象(如静态变量、长期运行的线程)引用
 *
 * 本例演示Handler导致的内存泄漏场景:
 */
public class MemoryLeakAct extends AppCompatActivity {
    private static final String TAG = "MemoryLeakAct";

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

        // 场景分析:Handler持有Runnable,Runnable持有Activity
        // Handler是主线程的Handler(属于GCRoot),会一直存在于内存中
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                // 这里使用MemoryLeakAct.this,导致Runnable隐式持有Activity引用
                Context context = MemoryLeakAct.this;
                WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                int width = windowManager.getDefaultDisplay().getWidth();
                Log.d(TAG, "屏幕宽度: " + width);
            }
        }, 60_000); // 延迟60秒执行

        /**
         * 三种操作场景的结果分析:
         *
         * 场景1:打开Activity后立即关闭(<60秒)
         * - Activity被Runnable持有 → 无法回收 → 内存泄漏
         * - 60秒后Runnable执行完毕 → 引用释放
         * - 下次GC时Activity被回收 → 泄漏结束
         *
         * 场景2:打开Activity,等待60秒后再关闭
         * - 60秒内Runnable已执行完 → 引用链已断开
         * - 关闭Activity时无额外引用 → 正常回收
         * - 不会发生内存泄漏
         *
         * 场景3:反复快速打开关闭Activity
         * - 多个Activity实例被多个待执行的Runnable持有
         * - 每个Activity都要在内存中存活至少60秒
         * - 可能导致内存暴增甚至OOM
         */
    }

    /**
     * 正确的做法:在onDestroy中移除未执行的回调
     */
    private Handler handler = new Handler();

    @Override
    protected void onDestroy() {
        handler.removeCallbacksAndMessages(null); // 移除所有回调和消息
        super.onDestroy();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读