在這里先說一下在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 的思想,