内存泄漏—出现情况,非静态内部类对外部类引用持有的泄漏复现

前言

  • 本文为制造一个”非静态内部类对外部类的引用持有”泄漏并对其结果进行观察作为学习使用,手段是制造泄漏,目的是了解泄漏产生的原因并未解决提供一种思路。
  • 本文基于的思想是:2个Activity,其中一个Activity的内部类被外部引用挂住,导致该Acitvity无法正常回收。
  • 本文只是对泄漏测试代码的一个讲解,并没有涉及到泄漏排查工具的使用,结合工具使用我会另开一片文章,https://blog.csdn.net/user11223344abc/article/details/80319915

Code

俩个Activity,一个SplashActivity,一个LeakActivity。操作路径是从Splash跳到LeakActivity。往返5次之后,手动Gc。

SplashActivity


    package zj.com;

    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;

    import zj.com.rxjava_operators.R;

    public class SplashActivity extends AppCompatActivity {

        private LeakActivity.TestResource testResource;
        private LeakActivity.TestResource testResource2;

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


            findViewById(R.id.leakgo).setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    startActivity(new Intent(SplashActivity.this, LeakActivity.class));
                    if (null == testResource)
                        testResource = new LeakActivity().new TestResource(1, "张三");

                    if (null == testResource2)
                        testResource2 = new LeakActivity().new TestResource(2, "李四");
                }

            });
        }

    }

LeakActivity


    package zj.com;

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;

    import zj.com.rxjava_operators.R;

    public class LeakActivity extends AppCompatActivity {

        private  TestResource mResource = null;

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

             mResource = new TestResource();

            //...
        }

        class TestResource {

            public TestResource() {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }


            //        //...
            int age;
            String name;

            public TestResource(int age, String name) {
                this.age = age;
                this.name = name;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }

            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

        }
    }

代码分析

  • 可以看到,整个内存泄漏是由于LeakActivity的”非静态内部类”引起的。
  • 你也注意到了,整个内部类TestResource,看起来挺别扭的,空参构造内还开个线程休息10秒,我为啥要这么写?
  • 我测试时,遇见俩种情况使这个内存泄漏无法复现,(1)当我空参构造休眠1秒时,(2)当我的TestResource内部类里面啥都没写时。为什么?具体太细我也说不上,这个可能要从GC的算法判断和回收的机制说起,这里我先TODO,简单分析下,我觉得应该是JVM一个内部的机制(据说是指令优化?),使得GC回收监测到上述俩种情况就会将这类对象去回收掉,从而无法引起内存泄漏,但是值得注意的一点是,当我在空参构造内睡1秒而非是10秒时,GC这时会去回收这个对象,而10秒时候则不会去回收,我觉得应该是GC监测到了该内部类对象还有未执行完毕的任务时,就不会去回收。
  • 再看SplashActivity,每次点击的时候,我都会创建俩个TestResource的对象,这俩个对象主要是让SplashActivity这个类从外部的因素去引用TestResource这个引用,从而引起内存泄漏。

总结

总结下,如果我们想要制造有内部类引起的内存泄漏:

  • 那么这个内部类一定不能为空类
  • 在没有外部影响的情况下,比如我在Splash内对Leak的内部类对象进行引用时,我们这时候就要让Leak.TestResource拥有足够的处理时间。
  • 在有外部因素的影响下,这时候我们则可以不必考虑让TestResouce去长时间处理一个任务也可以复现内存泄漏。
  • 本文Demo地址

猜你喜欢

转载自blog.csdn.net/user11223344abc/article/details/80318773
今日推荐