Android 变量生命周期、变量内存释放机制、GC触发时机研究

2022-04-21  本文已影响0人  大鼓书

Android的GC机制是可达性回收,具体本文就不再具体阐述了,本文只分析android系统什么时候会触发GC,以及监听Object对象被回收的时机:

先看下面的代码的注释,先明白我说的全局变量 局部变量 说的是什么意思

class DetailActivity : AppCompatActivity() {
    //这个house就是全局变量
    private var house: House? = null
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_detail)
        //这个person就是局部变量
        val person = Person()
      }
  
     findViewById<TextView>(R.id.button).setOnClickListener { v->
            //这个person2就是局部变量
            person2 = Person()
            Log.e("dq","create Person "+person2!!.hashCode());
        }
}
com.dq.qkotlin: Background young concurrent copying GC freed 58306(1766KB) AllocSpace objects, 4(68KB) LOS objects, 32% free, 3959KB/5892KB, paused 5.216ms total 23.590ms

意外发现

//每调用一次创建十万个对象,检测内存变化
                for (int i = 0; i < 100000; i++) {
                    linklist.add(new Object()); //内存变化:73M - 76M - 79M  - 82M
                    linklist.add(new HashMap<String,String>()); //内存变化:79M - 85M - 91M

                    GiftBean bean = new GiftBean();
                    bean.setTitle("1");
                    linklist.add(bean); //71M - 77M - 83M - 88M - 94M = 每次差额为6M

                    HashMap<String,String> map = new HashMap<String,String>();
                    map.put("title","1");
                    linklist.add(map); //72M - 88M - 105M - 121M = 每次差额为17M
                }

如何监听变量生命周期?

class Person : Object() {

    var name: String? = null

    //走了finalize方法就说明该Object被回收了
    @kotlin.jvm.Throws(Throwable::class)
    override fun finalize() {
        Log.e("dq","Person 被回收 " +hashCode())
    }
}

如何监听系统GC?

public class GcWatcherInternal {

    private static WeakReference<GcWatcher> sGcWatcher;

    private static ArrayList<Runnable> sGcWatchers = new ArrayList<>();
    private static Object lock = new Object();

    private static final class GcWatcher {
        @Override
        protected void finalize() throws Throwable {
            sLastGcTime = SystemClock.uptimeMillis();
            ArrayList<Runnable> sTmpWatchers;
            synchronized (lock) {
                sTmpWatchers = sGcWatchers;
                try {
                    for (int i = 0; i < sTmpWatchers.size(); i++) {
                        if (sTmpWatchers.get(i) != null) {
                            sTmpWatchers.get(i).run();
                        }
                    }
                } catch (Throwable e){
                    e.printStackTrace();
                }
                sGcWatcher = new WeakReference<>(new GcWatcher());
            }
        }
    }

    public static void addGcWatcher(Runnable watcher) {
        synchronized (lock) {
            sGcWatchers.add(watcher);
            if(sGcWatcher==null)
                sGcWatcher = new WeakReference<>(new GcWatcher());
        }
    }
}
//MainActivity中写一次就好
GcWatcherInternal.addGcWatcher { Log.e("dq","触发GC !!!") }

我们可以怎么优化内存

网上别人写的什么bitmap、handle内存泄漏、静态单例 一堆乱七八糟的东西,我在这里就不写了。我就只针对上述我自己的研究成果说一下我自己的看法

一些恶劣的代码,以及会产生什么情况

事先声明:网上别人写的什么handle内存泄漏、静态单例 一堆乱七八糟的东西,我在这里就不写了。我就只针对上述我自己的研究成果说一下我自己的看法

以下代码是正常使用的情况,他可以正常释放:


    private val handler = MyHandler(this)

    private class MyHandler(context: Context) : Handler(Looper.getMainLooper()) {

        private val reference: WeakReference<Context> = WeakReference(context)

        override fun handleMessage(msg: Message) {

            val activity = reference.get() as DetailActivity?
          //经过测试:onDestory后5秒触发GC,然后就Activity = 0。且全局变量house也被释放
            Log.e("dz","MyHandler收到消息 Activity = "+System.identityHashCode(activity));

            if (activity == null){
                return
            }

            when (msg.what) {
                1 -> {
                    Log.e("dz","MyHandler收到消息 且 Activity没死 "+activity.house); //每0.4秒打印一次,直到onDestory的5秒后触发了GC,就会被上面的activity == null拦截
                    activity.handler.sendEmptyMessageDelayed(1 , 400)
                }
            }
        }
    }


    Thread(Runnable {
            Thread.sleep(2000)
             Log.e("dz","给activity 扔 start " +  house.hashCode() +" Activity = "+ this@DetailActivity.hashCode());
                  //LOG:给activity 扔 start 210397411 Activity = 237971341
             handler.sendEmptyMessageDelayed(1 , 400)
     }).start()
上一篇 下一篇

猜你喜欢

热点阅读