Android 性能優(yōu)化之內存抖動

在這里先說一下在android 系統(tǒng)中使用最廣泛的防止內存抖動的一個機制 Message

在android 系統(tǒng)中 Message 是使用最頻繁的一個類之一了,整個系統(tǒng)的運行都是建立在 Message 只上的,那么為了防止頻繁的創(chuàng)建和銷毀對象,Message 又是使用的什么方式來防止內存抖動的呢

1.對象緩存池

  public static Message obtain() {
      synchronized (sPoolSync) {
          if (sPool != null) {
              Message m = sPool;
              sPool = m.next;
              m.next = null;
              m.flags = 0; // clear in-use flag
              sPoolSize--;
              return m;
          }
      }
      return new Message();
  }


    void recycleUnchecked() {
      // Mark the message as in use while it remains in the recycled object pool.
      // Clear out all other details.
      flags = FLAG_IN_USE;
      what = 0;
      arg1 = 0;
      arg2 = 0;
      obj = null;
      replyTo = null;
      sendingUid = UID_NONE;
      workSourceUid = UID_NONE;
      when = 0;
      target = null;
      callback = null;
      data = null;

      synchronized (sPoolSync) {
          if (sPoolSize < MAX_POOL_SIZE) {
              next = sPool;
              sPool = this;
              sPoolSize++;
          }
      }
  }

Message 內部有一個叫做 是Message 的next 屬性,用來記錄下一個Message 內容,還有一個 sPool 的靜態(tài)變量,用來保存當前緩存的頭結點,那么這就形成了一個由無用消息組成的單鏈表
由于是無用消息,我們不關心這個消息的內容,所以非常適合現(xiàn)在這種場景

適用場景
在循環(huán)中創(chuàng)建重復的對象 , 在onDraw 中組裝繪制對象 , ,生產消費模式也比較使用這種情況
下面我們句一個小例子來看一下profiler 中創(chuàng)建了多少個對象

class MainActivity : AppCompatActivity() {
  private var  isRunning=false
  private val queue: LinkedBlockingQueue<TsmMessage> = LinkedBlockingQueue<TsmMessage>()
  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)
      findViewById<View>(R.id.tv_click).setOnClickListener {
          isRunning=true
          test()
      }
      findViewById<View>(R.id.tv_end).setOnClickListener {
          isRunning=false
      }
  }
  fun test(){
      thread {
          while (isRunning){
              kotlin.runCatching {
                  queue.add(TsmMessage())
                  sleep(2)
              }
          }
      }
      thread {

          while (isRunning){
              kotlin.runCatching {
                  queue.take()
                sleep(2)
              }
          }
      }
  }
}

在這4秒多的時間里面我們一共創(chuàng)建了9872 個 TsmMessage 的對象


點擊一個TsmMessage 可以看到 這個對象的創(chuàng)建和運行的位置,kotlin 有點不準確,但是足夠用了


再來看一下我們優(yōu)化后的效果


從這里我們可以看到 在這個時間段里面,他只創(chuàng)建了12個對象,大大減少了我們創(chuàng)建對象的個數(shù)

在項目中比較常見的 String+="abc"

相信絕大多數(shù)人在看了這段代碼都知道 string + "abc" 都知道jvm 會為我們創(chuàng)建重復的對象 ,如果這句話寫在了 循環(huán)中 也會造成內存抖動

對于數(shù)組 byte[] 數(shù)組的回收 Glide Array數(shù)據(jù)回收機制 LruArrayPool

這個類的路徑是 package com.bumptech.glide.load.engine.bitmap_recycle; LruArrayPool

先來說一下他這個類的思想

先來說一下他的put 方法,比較簡單
用這個數(shù)據(jù)的lenght 作為key 放入到map 中, 之后將這個lenght 的數(shù)據(jù)個數(shù)放入到treemap 中,整體的邏輯就是這樣的

再來說一下他的get 方法,思想特別神奇,
入?yún)⑹?length ,他的意思是我要獲取一個length 的Array 數(shù)據(jù),他會先去treemap 中獲取一個等于或者大于length 最接近這個長度的數(shù)組key,再根據(jù)這個key去map中拿到一個鏈表中的數(shù)據(jù),最后將treemap 的length 的數(shù)據(jù)個數(shù)-1,整個過程完成

他的非常重要的思想就是你想獲取一個長度為length 的 array ,我返回給你一個大于或者等于這個length 的array ,滿足你的需求

對于數(shù)組的回收 Glide Array數(shù)據(jù)回收機制 LruArrayPool 弊端

再來說一下這個方法的弊端

private final Map<Class<?>, NavigableMap<Integer, Integer>> sortedSizes = new HashMap<>();

弄了一個簡易的測試方法發(fā)現(xiàn),在使用Interger 做了Map 的key ,但是我們實際傳入的是int 類型,在get 與 put 的過程中 jvm 就會自動實現(xiàn) 對象拆箱裝箱的這個操作,也創(chuàng)建了非常多的對象,那么如何去優(yōu)化他呢, int 作為key 的 Array ,沒錯 就是SparseArray 的思想,

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容