当前位置:   article > 正文

Android 引起内存泄漏的几种情况_objectwatcher was watching this

objectwatcher was watching this

概述

内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。内存泄露有时不严重且不易察觉,这样开发者就不知道存在内存泄露,但有时也会很严重,会提示你Out of memory。 

首先使用leakcanary来检测内存泄漏

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'

使用当前版本的leakcanary无需install直接可以使用(下一篇文章将会具体讲解leakcanary的使用及其原理)

第一种非静态内部类创建静态实例导致的内存泄漏

非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。

  1. public class TestLeakActivity extends Activity {
  2. private static TestResource mResource = null;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. // MyApplication.getRefWatcher(this).watch(this);
  7. setContentView(R.layout.activity_test_leak);
  8. initData();
  9. }
  10. private void initData() {
  11. if (mResource == null){
  12. mResource = new TestResource();
  13. }
  14. findViewById(R.id.finish_btn).setOnClickListener(new View.OnClickListener() {
  15. @Override
  16. public void onClick(View v) {
  17. finish();
  18. }
  19. });
  20. }
  21. private class TestResource{
  22. }
  23. }

当应用启动之后,关闭按钮发现leakcanary打印出如下(No是没有Yes是有内存的泄漏,UNKNOWN表示可能出现了内存泄漏)

  1. ====================================
  2. HEAP ANALYSIS RESULT
  3. ====================================
  4. 1 APPLICATION LEAKS
  5. References underlined with "~~~" are likely causes.
  6. Learn more at https://squ.re/leaks.
  7. 105602 bytes retained by leaking objects
  8. Signature: d8d8d1ffbf342e9bb5d82c1f67b33795b6105
  9. ┬───
  10. │ GC Root: Local variable in native code
  11. ├─ android.os.HandlerThread instance
  12. │ Leaking: NO (PathClassLoader↓ is not leaking)
  13. │ Thread name: 'LeakCanary-Heap-Dump'
  14. │ ↓ HandlerThread.contextClassLoader
  15. ├─ dalvik.system.PathClassLoader instance
  16. │ Leaking: NO (TestLeakActivity↓ is not leaking and A ClassLoader is never leaking)
  17. │ ↓ PathClassLoader.runtimeInternalObjects
  18. ├─ java.lang.Object[] array
  19. │ Leaking: NO (TestLeakActivity↓ is not leaking)
  20. │ ↓ Object[].[519]
  21. ├─ com.example.myapplication.TestLeakActivity class
  22. │ Leaking: NO (a class is never leaking)
  23. │ ↓ static TestLeakActivity.mResource
  24. │ ~~~~~~~~~
  25. ├─ com.example.myapplication.TestLeakActivity$TestResource instance
  26. │ Leaking: UNKNOWN
  27. │ Retaining 105614 bytes in 1636 objects
  28. this$0 instance of com.example.myapplication.TestLeakActivity with mDestroyed = true
  29. │ ↓ TestLeakActivity$TestResource.this$0
  30. │ ~~~~~~
  31. ╰→ com.example.myapplication.TestLeakActivity instance
  32. ​ Leaking: YES (ObjectWatcher was watching this because com.example.myapplication.TestLeakActivity received
  33. ​ Activity#onDestroy() callback and Activity#mDestroyed is true)
  34. ​ Retaining 105602 bytes in 1635 objects
  35. ​ key = 6382ce72-b981-4a84-a5f7-fd1971479092
  36. ​ watchDurationMillis = 12949
  37. ​ retainedDurationMillis = 7919
  38. ​ mApplication instance of com.example.myapplication.MyApplication
  39. ​ mBase instance of android.app.ContextImpl, not wrapping known Android context
  40. ====================================
  41. 0 LIBRARY LEAKS
  42. A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
  43. See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
  44. ====================================

从GC ROOTS向下搜索,一直去找引用链,如果某一个对象跟GC Roots没有任何引用链相连时,就证明对象是”不可达“的,可以被回收。

打印出来的log发现在TestLeakAcitvity执行完ondestory是有一个UNKNOWN内存的泄漏TestResource.this

解决方案

可以将非静态内部类改成静态内部类

  1. private static class TestResource{
  2. }

第二种单例模式导致的内存泄漏

创建一个单例

  1. public class Singleton {
  2. private static Singleton sInstance;
  3. private Context mContext;
  4. private Singleton(Context context) {
  5. this.mContext = context;
  6. }
  7. public static Singleton getInstance(Context context) {
  8. if (sInstance == null) {
  9. sInstance = new Singleton(context);
  10. }
  11. return sInstance;
  12. }
  13. public void test(){
  14. mContext.getContentResolver();
  15. }
  16. }

在Activity中执行

  1. public class TestLeakActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_test_leak);
  6. Singleton.getInstance(this).test();
  7. }
  8. }

执行完单例之后看一下leakcanary

  1. LeakCanary: 1 APPLICATION LEAKS
  2. LeakCanary: ┬───
  3. LeakCanary: │ GC Root: Local variable in native code
  4. LeakCanary: │ ...
  5. LeakCanary: ├─ com.example.myapplication.Singleton instance
  6. LeakCanary: │ Leaking: UNKNOWN
  7. LeakCanary: │ Retaining 112915 bytes in 1674 objects
  8. LeakCanary: │ mContext instance of com.example.myapplication.TestLeakActivity with mDestroyed = true
  9. LeakCanary: │ ↓ Singleton.mContext
  10. LeakCanary: │ ~~~~~~~~
  11. LeakCanary: ╰→ com.example.myapplication.TestLeakActivity instance
  12. LeakCanary: ​ Leaking: YES (ObjectWatcher was watching this because com.example.myapplication.TestLeakActivity received
  13. LeakCanary: ​ Activity#onDestroy() callback and Activity#mDestroyed is true)
  14. LeakCanary: ​ Retaining 112903 bytes in 1673 objects
  15. LeakCanary: ​ key = 297d72a4-5e9d-41bf-baba-6856105c73f0
  16. LeakCanary: ​ watchDurationMillis = 5176
  17. LeakCanary: ​ retainedDurationMillis = 139
  18. LeakCanary: ​ mApplication instance of com.example.myapplication.MyApplication
  19. LeakCanary: ​ mBase instance of android.app.ContextImpl, not wrapping known Android context
  20. LeakCanary: ====================================
  21. LeakCanary: 0 LIBRARY LEAKS

发现UNKNOW 出现地方为Singleton中的mContext,说明当前的mContext可能没有释放掉,但是后续又看到YES说明当前确实没有释放掉

解决方案

将context变成ApplicationContext,当应用关掉之后,会自动回收ApplicationContext

  1. private Singleton(Context context) {
  2. this.mContext = context.getApplicationContext();
  3. }

第三种动画

  1. public class TestLeakActivity extends Activity {
  2. private ImageView mAlphaImage;
  3. private ValueAnimator warningAnimation;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_test_leak);
  8. initData();
  9. startValueAnimatorAnim(mAlphaImage);
  10. }
  11. private void initData() {
  12. mAlphaImage = (ImageView)findViewById(R.id.alphaImage);
  13. findViewById(R.id.finish_btn).setOnClickListener(new View.OnClickListener() {
  14. @Override
  15. public void onClick(View v) {
  16. onBackPressed();
  17. }
  18. });
  19. }
  20. /**
  21. * 在一段时间内生成连续的值完成view的缩放
  22. * @param v
  23. */
  24. public void startValueAnimatorAnim(final View v) {
  25. //不改变属性大小,只在一段事件内生成连续的值
  26. ValueAnimator animator = ValueAnimator.ofFloat(0f, 100f);
  27. animator.setDuration(50000);
  28. animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  29. @Override
  30. public void onAnimationUpdate(ValueAnimator animation) {
  31. //百分比对应的值
  32. float value = (float) animation.getAnimatedValue();
  33. v.setScaleX(0.5f + value / 200);
  34. v.setScaleY(0.5f + value / 200);
  35. }
  36. });
  37. animator.start();
  38. }
  39. }

发生的条件是在50秒内关闭当前应用,就会出现内存泄漏 

  1. LeakCanary: Found 1 paths to retained objects
  2. ...
  3. LeakCanary: ┬───
  4. LeakCanary: │ GC Root: System class
  5. LeakCanary: │
  6. LeakCanary: │ ...
  7. LeakCanary: ├─ com.example.myapplication.TestLeakActivity$3 instance
  8. LeakCanary: │ Leaking: UNKNOWN
  9. LeakCanary: │ Anonymous class implementing android.animation.ValueAnimator$AnimatorUpdateListener
  10. LeakCanary: │ ↓ TestLeakActivity$3.this$0
  11. LeakCanary: │ ~~~~~~
  12. LeakCanary: ╰→ com.example.myapplication.TestLeakActivity instance
  13. LeakCanary: ​ Leaking: YES (ObjectWatcher was watching this because com.example.myapplication.TestLeakActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)

说明该动画的AnimatorUpdateListener 监听还持有TestLeakActivity

解决方案

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. if (animator!=null){
  5. animator.cancel();
  6. }
  7. }

原因是当前还持有事件监听的引用,执行完cancel会去清除当前监听的引用。

第四种Handler导致的内存泄漏

本地测试发现,这种是不一定会内存泄漏的,除非MessageQueue还持有当前应用的activity 才会内存泄漏,具体出现原因以及修改方案我从网上找到一篇写的比较好的文章

路径如下(Handler为什么可能会造成内存泄漏以及可用的四种解决方法

第五种匿名内部类引起的内存泄漏

  1. public class TestLeakActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_test_leak);
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. try {
  10. //模拟耗时操作
  11. Thread.sleep(15000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }).start();
  17. }
  18. }

发生条件在15秒内关闭程序就会发生内存泄漏

  1. LeakCanary: 1 APPLICATION LEAKS
  2. LeakCanary: ┬───
  3. LeakCanary: │ GC Root: Local variable in native code
  4. LeakCanary: │
  5. LeakCanary: ├─ java.lang.Thread instance
  6. LeakCanary: │ Leaking: UNKNOWN
  7. LeakCanary: │ Thread name: 'Thread-1'
  8. LeakCanary: │ ↓ Thread.target
  9. LeakCanary: │ ~~~~~~
  10. LeakCanary: ├─ com.example.myapplication.TestLeakActivity$1 instance
  11. LeakCanary: │ Leaking: UNKNOWN
  12. LeakCanary: │ Anonymous class implementing java.lang.Runnable
  13. LeakCanary: │ ↓ TestLeakActivity$1.this$0
  14. LeakCanary: │ ~~~~~~
  15. LeakCanary: ╰→ com.example.myapplication.TestLeakActivity instance
  16. LeakCanary: ​ Leaking: YES (ObjectWatcher was watching this because com.example.myapplication.TestLeakActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)

说明当前Runnable 还持有TestLeakActivity

解决方案

  1. public class TestLeakActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_test_leak);
  6. new Thread(new MyRunnable()).start();
  7. }
  8. private static class MyRunnable implements Runnable {
  9. @Override
  10. public void run() {
  11. try {
  12. Thread.sleep(15000);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }
  18. }

静态内存类不会持有当前的引用

推荐内部类(AsyncTask Timer 等内存泄漏文章例子:android-内部类导致的内存泄漏实战解析

第六种对象的注册但是没有反注册导致的内存泄漏

  1. public class TestLeakActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_test_leak);
  6. initData();
  7. }
  8. @Override
  9. protected void onStart() {
  10. super.onStart();
  11. EventBus.getDefault().register(this);
  12. }
  13. @Subscribe(threadMode = ThreadMode.MAIN)
  14. public void onMessageEvent(MessageEvent event) {
  15. Log.i("LeakCanary", "message is " + event.getMessage());
  16. // 更新界面
  17. textView.setText(event.getMessage());
  18. }
  19. private void initData() {
  20. textView = (TextView)findViewById(R.id.text);
  21. findViewById(R.id.finish_btn).setOnClickListener(new View.OnClickListener() {
  22. @Override
  23. public void onClick(View v) {
  24. EventBus.getDefault().post(new MessageEvent("Hello EventBus!"));
  25. onBackPressed();
  26. }
  27. });
  28. }
  29. }

发生条件注册EventBus 但是没有反注册

  1. D LeakCanary: 1 APPLICATION LEAKS
  2. D LeakCanary: ┬───
  3. D LeakCanary: │ GC Root: Local variable in native code
  4. D LeakCanary: ├─ org.greenrobot.eventbus.EventBus class
  5. D LeakCanary: │ Leaking: NO (a class is never leaking)
  6. D LeakCanary: │ ↓ static EventBus.defaultInstance
  7. D LeakCanary: │ ~~~~~~~~~~~~~~~
  8. D LeakCanary: ├─ org.greenrobot.eventbus.EventBus instance
  9. D LeakCanary: │ Leaking: UNKNOWN
  10. D LeakCanary: │ ↓ EventBus.typesBySubscriber
  11. D LeakCanary: │ ~~~~~~~~~~~~~~~~~
  12. D LeakCanary: ├─ java.util.HashMap instance
  13. D LeakCanary: │ Leaking: UNKNOWN
  14. D LeakCanary: │ ↓ HashMap.table
  15. D LeakCanary: │ ~~~~~
  16. D LeakCanary: ├─ java.util.HashMap$Node[] array
  17. D LeakCanary: │ Leaking: UNKNOWN
  18. D LeakCanary: │ ↓ HashMap$Node[].[0]
  19. D LeakCanary: │ ~~~
  20. D LeakCanary: ├─ java.util.HashMap$Node instance
  21. D LeakCanary: │ Leaking: UNKNOWN
  22. D LeakCanary: │ ↓ HashMap$Node.key
  23. D LeakCanary: │ ~~~
  24. D LeakCanary: ╰→ com.example.myapplication.TestLeakActivity instance
  25. D LeakCanary: ​ Leaking: YES (ObjectWatcher was watching this because com.example.myapplication.TestLeakActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)

解决方案

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. EventBus.getDefault().unregister(this);
  5. }

像这种反注册的例子(广播,otto,以及ContentProvider等等,都需要在onDestory里面反注册) 

第七种静态集合引发的内存泄漏

  1. public class TestLeakActivity extends Activity {
  2. private static List<Activity> list;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_test_leak);
  7. list = new ArrayList<>();
  8. for (int i=0;i<100;i++){
  9. list.add(this);
  10. }
  11. }
  12. }

当关闭应用程序发生内存泄漏

  1. LeakCanary: 1 APPLICATION LEAKS
  2. LeakCanary: ┬───
  3. LeakCanary: │ GC Root: Local variable in native code
  4. LeakCanary: ├─ java.util.ArrayList instance
  5. LeakCanary: │ Leaking: UNKNOWN
  6. LeakCanary: │ ↓ ArrayList.elementData
  7. LeakCanary: │ ~~~~~~~~~~~
  8. LeakCanary: ├─ java.lang.Object[] array
  9. LeakCanary: │ Leaking: UNKNOWN
  10. LeakCanary: │ ↓ Object[].[0]
  11. LeakCanary: │ ~~~
  12. LeakCanary: ╰→ com.example.myapplication.TestLeakActivity instance
  13. LeakCanary: ​ Leaking: YES (ObjectWatcher was watching this because com.example.myapplication.TestLeakActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)

 发现当前ArrayList持有Activity

解决方案

  1. protected void onDestroy() {
  2. if (list!=null){
  3. list.clear();
  4. }
  5. }

第八种资源未关闭

本地测试没有复现出来内存泄漏,可能是我的资源比较少,或者比较小,释放的快

在android中,资源性对象比如Cursor、File、Bitmap、视频等,系统都用了一些缓冲技术,在使用这些资源的时候,如果我们确保自己不再使用这些资源了,要及时关闭,否则可能引起内存泄漏。因为有些操作不仅仅只是涉及到Dalvik虚拟机,还涉及到底层C/C++等的内存管理,不能完全寄希望虚拟机帮我们完成内存管理。

以上就是总结的内存泄漏!下一章将讲解LeakCanary的使用,及其原理

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/240227
推荐阅读
相关标签
  

闽ICP备14008679号