一般什么情况下会导致内存泄漏问题?
基本知识储备
-
内存泄漏
程序不再使用对象 A,但是因为 A 持有另外一个生命周期更长的对象 B 的引用,导致 A 不能及时被回收,一直占用着内存
-
内存溢出
内存泄漏情况严重,因为占用情况导致无法被回收的对象越来越多,直到程序对内存的总需求超过了系统给分配的内存空间大小
-
Java 垃圾回收机制(待填坑)
总结来说,只需要记住一点:长生命周期的对象持有短生命周期对象的引用,就会导致内存泄漏;过多的内存泄漏,就会导致内存溢出。
Android 中常见的内存泄漏场景
-
类的静态变量
静态变量生命周期与应用相当,Activity 中一个静态的 View 有可能以
new View(this)
的方式持有 Activity 的引用,这种情况会导致对应的 Activity 永远不能被正确回收 -
内部类持有外部类引用
-
匿名内部类
当匿名内部类和异步任务一起出现时,可能发生内存泄漏。当 Activity 关闭时,如果子线程的异步任务还没有完成,那对应的 Activity 就会泄漏
-
非静态内部类
常见的就是
Handler
的错误使用。public Handler mHandler = new Handler(){ public void handleMessage(Message msg){} }
Handler 持有当前 Activity 引用,它发出的 Message 又持有 Handler 的引用,消息队列持有 Message 的引用,一层套一层,导致 Activity 在关闭后也不能被释放,就造成了内存泄漏。
-
-
资源对象未关闭
比较典型的如
Cursor
、File
、Stream
、ContentProvider
等,资源型对象往往都用了一些缓冲,在不使用时,应该及时关闭 -
注册对象未注销
常见的有 EventBus,广播等,一般都要在 onDestory() 中取消注册
-
容器没有及时清理
集合等容器类有可能被声明为静态,或者存储着静态数据;一般来讲容器占用内存也比较大,应该及时清理
-
Context 泄漏
常见于将 Activity 级别的 Context 错误地传递到耗时任务中,导致因生命周期不一致导致的内存泄漏出现
-
WebView
org.chromium.android_webview.AwContents
类中注册了component callbacks
,但是未正常反注册
内存泄漏的补救措施
很简单,内存泄漏的病根在于生命周期长度的不一致,对症下药即可。
-
尽量避免使用静态成员变量
-
不要在匿名内部类中进行耗时操作
-
将非静态内部类转为静态内部类,如果需要外部引用,考虑使用弱引用
Handler 内存泄漏的处理方案
-
在 Activity 的 onDestory 回调中做好收尾工作
- 移除 Handler 的消息
-
静态变量置为空,取消注册,取消异步任务
- 及时关闭资源对象
-
及时清理容器,将集合里的东西clear,然后置为null
-
在进行耗时操作时,使用 applicationContext 而不是 Activity 的 Context
-
WebView
-
在代码中使用
new
关键字而不是在布局文件中直接声明 WebView 节点 -
手动删除引用
public void setConfigCallback(WindowManager windowManager) { try { Field field = WebView.class.getDeclaredField("mWebViewCore"); field = field.getType().getDeclaredField("mBrowserFrame"); field = field.getType().getDeclaredField("sConfigCallback"); field.setAccessible(true); Object configCallback = field.get(null); if (null == configCallback) { return; } field = field.getType().getDeclaredField("mWindowManager"); field.setAccessible(true); field.set(configCallback, windowManager); } catch(Exception e) { } }
然后在 Activity 中调用:
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setConfigCallback(WindowManager)getApplicationContext() .getSystemService(Context.WINDOW_SERVICE)); } public void onDestroy() { setConfigCallback(null); super.onDestroy(); }
- 从根源上解决
@Override protected void onDestroy() { if( mWebView!=null) { // org.chromium.android_webview.AwContents#onDetachedFromWindow() // if (isDestroyed()) return; ViewParent parent = mWebView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(mWebView); } mWebView.stopLoading(); mWebView.getSettings().setJavaScriptEnabled(false); mWebView.clearHistory(); mWebView.clearView(); mWebView.removeAllViews(); mWebView.destroy(); } // 在 onDestroy 之前先从父布局移除掉 WebView,否则无效 super.onDestroy(); }
-
-
优化数据结构
- 优先使用ArrayMap 和基本类型,而非 HashMap 和包装类;
- 枚举在 Android 中占用内存较大,应该尽量避免使用;
- 学习使用 LruCache 和 SparseArray 等类
- 使用 RGB_8888 代替 RGB_565
网友评论