内存泄漏示例(解释)
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();
}
}