Android 内存泄漏详解

Android 内存泄漏详解
一、什么是内存泄漏内存泄漏Memory Leak是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放造成系统内存的浪费导致程序运行速度减慢甚至系统崩溃等严重后果。在 Android 中更具体的定义是长生命周期对象持有短生命周期对象的引用导致短生命周期对象无法被垃圾回收器GC回收。二、内存泄漏的危害危害说明OOM 崩溃内存持续增长最终触发OutOfMemoryError应用崩溃卡顿/ANRGC 频繁触发主线程被阻塞界面卡顿甚至 ANR电量消耗后台泄漏导致进程无法被回收持续占用 CPU 和内存用户体验差应用越来越卡需要频繁重启三、导致内存泄漏的常见情况1.静态变量持有 Activity/Context 引用⭐最常见javapublic class Singleton { private static Singleton instance; private Context context; private Singleton(Context context) { this.context context; // ❌ 持有 Activity 引用 } public static Singleton getInstance(Context context) { if (instance null) { instance new Singleton(context); } return instance; } }原因静态变量生命周期 应用生命周期如果传入的是 Activity ContextActivity 销毁后仍被静态引用持有无法回收。解决使用Application Contextcontext.getApplicationContext()。2.非静态内部类 / 匿名内部类持有外部类引用javapublic class MainActivity extends AppCompatActivity { // ❌ 非静态内部类隐式持有外部 Activity 引用 private class MyHandler extends Handler { Override public void handleMessage(Message msg) { // ... } } // ❌ 匿名内部类同样持有 Activity 引用 private Runnable runnable new Runnable() { Override public void run() { // 延迟执行Activity 可能已销毁 } }; }原因非静态内部类会隐式持有外部类的引用。如果内部类有耗时操作如 Handler 延迟消息、AsyncTask、ThreadActivity 销毁时无法被回收。解决使用静态内部类弱引用WeakReference在onDestroy()中移除消息/回调javaprivate static class MyHandler extends Handler { private WeakReferenceMainActivity weakRef; MyHandler(MainActivity activity) { weakRef new WeakReference(activity); } Override public void handleMessage(Message msg) { MainActivity activity weakRef.get(); if (activity ! null) { // 安全使用 } } } Override protected void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null); // 移除所有消息 }3.Handler 内存泄漏⭐高频考点javapublic class MainActivity extends AppCompatActivity { private Handler handler new Handler(Looper.getMainLooper()) { Override public void handleMessage(Message msg) { // 隐式持有 MainActivity 引用 } }; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handler.sendEmptyMessageDelayed(0, 10000); // 10秒后执行 } }原因Message会持有Handler引用Handler持有Activity引用MessageQueue持有Message。如果 Activity 在消息执行前被销毁整条引用链导致 Activity 泄漏。解决静态内部类 WeakReference onDestroy()移除消息。4.资源未关闭java// ❌ 未关闭的资源 Cursor cursor getContentResolver().query(...); // 忘记 cursor.close(); Bitmap bitmap BitmapFactory.decodeResource(...); // 忘记 bitmap.recycle(); // Android 3.0 后 Bitmap 内存由 GC 管理但大 Bitmap 仍建议及时释放 InputStream is ...; // 忘记 is.close();解决使用try-finally或在 Kotlin 中使用use自动关闭。5.监听器未注销javapublic class MainActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ❌ 注册后未在 onDestroy 中注销 getSystemService(LocationManager.class) .requestLocationUpdates(..., listener); } }常见场景LocationManager.requestLocationUpdates()未removeUpdates()SensorManager.registerListener()未unregisterListener()BroadcastReceiver动态注册后未unregisterReceiver()EventBus/RxJava订阅后未取消订阅解决在onDestroy()/onStop()中及时注销。6.单例模式持有回调/监听器javapublic class NetworkManager { private static NetworkManager instance; private NetworkCallback callback; public void setCallback(NetworkCallback callback) { this.callback callback; // ❌ 可能持有 Activity 引用 } }解决使用弱引用或在 Activity 销毁时手动置空。7.WebView 内存泄漏java// ❌ 直接在 xml 中声明 WebViewActivity 销毁时 WebView 持有 Activity 引用 WebView android:idid/webview android:layout_widthmatch_parent android:layout_heightmatch_parent /原因WebView 内部逻辑复杂会持有 Activity 引用且不易释放。解决使用独立的进程加载 WebViewandroid:process:web动态添加/移除 WebView销毁时从父布局移除并调用destroy()使用腾讯 X5 内核等第三方方案javaOverride protected void onDestroy() { if (webView ! null) { webView.loadDataWithBaseURL(null, , text/html, utf-8, null); webView.clearHistory(); ((ViewGroup) webView.getParent()).removeView(webView); webView.destroy(); webView null; } super.onDestroy(); }8.集合类泄漏javapublic class CacheManager { // ❌ 静态集合一直增长从未清理 private static ListObject cache new ArrayList(); public void add(Object obj) { cache.add(obj); } // 没有 remove() 或 clear() 机制 }解决使用LruCache等有限容量的缓存或设置过期清理机制。9.属性动画Animator未取消javaObjectAnimator animator ObjectAnimator.ofFloat(view, alpha, 0, 1); animator.setDuration(3000); animator.start(); // ❌ Activity 销毁时未取消动画原因动画会持有 target View 的引用View 持有 Activity 引用。解决在onDestroy()中animator.cancel()。10.系统服务持有 Contextjava// ❌ 错误 InputMethodManager imm (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);某些系统服务如InputMethodManager内部存在已知的内存泄漏 BugAndroid 底层问题。解决使用 Application Context 获取系统服务。11.第三方库使用不当Glide传入 Activity Context 时Glide 会绑定生命周期但如果传入 Application Context 且未手动管理可能泄漏RxJava订阅后未在onDestroy()中dispose()EventBus注册后未unregister()LeakCanary虽然它是检测工具但本身也可能有轻微影响四、内存泄漏检测工具表格工具用途LeakCanarySquare 出品自动检测并展示泄漏引用链开发必备Android Studio Profiler实时查看内存分配Heap Dump 分析MAT (Memory Analyzer Tool)分析 hprof 文件查找支配树和引用链adb shell dumpsys meminfo查看应用内存占用情况五、预防内存泄漏的黄金法则能用 Application Context 就不用 Activity Context静态变量不持有短生命周期对象内部类尽量声明为 static配合 WeakReference有注册就有注销有开启就有关闭耗时操作/延迟任务要考虑生命周期使用 Jetpack 组件ViewModel、Lifecycle、LiveData自动管理生命周期接入 LeakCanary 在开发阶段及时发现六、总结图plain长生命周期对象 ──持有引用──► 短生命周期对象 ↑ │ └───────────────────────────┘ 无法被 GC 回收 内存泄漏

最新新闻

日新闻

周新闻

月新闻