目標(biāo)
通常最占內(nèi)存的就是圖片,內(nèi)存優(yōu)化最優(yōu)先從圖片入手,排查下大內(nèi)存的圖片有哪些,是哪里產(chǎn)生的,是不是合理
定位
Android Studio → Dump Java Heap → 轉(zhuǎn)換格式 → 用MAT打開(kāi)
打開(kāi) Histogram 并按 Shallow Heap 排序。查看各類(lèi)型對(duì)象占用內(nèi)存情況,byte[] 遙遙領(lǐng)先,繼續(xù)看是什么對(duì)象產(chǎn)生的

去掉弱引用,查看GC Roots引用鏈


再按 Shallow Heap 排序,看到有12個(gè)占用內(nèi)存一模一樣的 byte[],大小是1048592字節(jié)=1M
我們看第一個(gè) byte[] 是什么,其余的同理


這個(gè)圖可以看出這個(gè) byte[] 其實(shí) Bitmap 的成員變量 mBuffer
這個(gè) bitmap 對(duì)象的所有成員變量都列在左側(cè)的 Attributes
對(duì)我們有用的信息是:這個(gè) bitmap 是512x512
繼續(xù)看這個(gè) bitmap 是被誰(shuí)持有的(定位我們自己的類(lèi))
這個(gè) bitmap 當(dāng)前有5個(gè)引用鏈,但未必全都是GCRoots可達(dá)的,我們?nèi)サ舨豢蛇_(dá)的


看到只有3條引用鏈了,全部展開(kāi)看看

看到有2條引用鏈都是Fresco的,肯定是圖片緩存相關(guān)的,暫不關(guān)心
第三條引用鏈信息:
RCDataChangeMonitor 成員變量 ArrayList mRCDataChangedListeners
ArrayList 成員變量 Object[] array,長(zhǎng)度為12(數(shù)組長(zhǎng)度12,并不代表每個(gè) index 都一定有值,末尾的可能尚未賦值,下文出現(xiàn)的數(shù)組也是如此)
array 的第3個(gè)元素是 CardPageVideoManager
CardPageVideoManager 成員變量 SimpleV2ListViewVideoScroller mVideoScroller
SimpleV2ListViewVideoScroller 成員變量 PinnedSectionListView mScrollView
PinnedSectionListView 成員變量 View[] mChildren,長(zhǎng)度為12(項(xiàng)目里 PinnedSectionListView extends ListView extends ViewGroup)

第4個(gè)是 LinearLayout (大概率所有元素都是LinearLayout)
LinearLayout 成員變量 View[] mChildren,長(zhǎng)度為12
第2個(gè)是 RelativeLayout
RelativeLayout 成員變量 mBaselineView 是 **QiyiDraweeView **
在項(xiàng)目里,上面出現(xiàn)的類(lèi)都是基礎(chǔ)類(lèi)(有太多類(lèi)在使用它們),無(wú)法得知是哪個(gè)具體的類(lèi)
我們想知道是哪個(gè)頁(yè)面,哪個(gè)控件,什么時(shí)候產(chǎn)生的這個(gè) Bitmap
第一種方法:斷點(diǎn)
- QiyiDraweeView 里 setController() 的時(shí)候有設(shè)置回調(diào)監(jiān)聽(tīng) setControllerListener(listener),那么我們就在回調(diào) onFinalImageSet() 里設(shè)置斷點(diǎn),并設(shè)置斷點(diǎn)條件 imageInfo.getWidth()==512 && imageInfo.getHeight()==512,當(dāng) Debug 停在這里的時(shí)候,我們就能拿到對(duì)應(yīng)的圖片 url

- 復(fù)制這個(gè) url,在 setController() 的調(diào)用方法里設(shè)置斷點(diǎn),并設(shè)置斷點(diǎn)條件 uri.toString().equals(圖片url)

- 當(dāng) Debug 停在此處,終于看到了具體的業(yè)務(wù)類(lèi) GameAndAppCardModel,之后就可以針對(duì)性地查看
GameAndAppCardModel 的布局信息以及代碼
可以在此處斷點(diǎn)查看這個(gè) QiyiDraweeView 的 width 和 height 是不是512x512,如果差距較大,那肯定是不合理的

第二種方法:繼續(xù)從 MAT 中挖掘信息
觀察 QiyiDraweeView 的 Attributes

比較有用的信息:
屬于 MainActivity,id=2131559271,measuredHeight 和 measuredWidth 都是155(猜測(cè)此處圖片加載是不合理的,控件是155x155,而加載的 Bitmap 是512x512,浪費(fèi)內(nèi)存,當(dāng)然以控件最終的 width 和 height 為準(zhǔn))
id 的16進(jìn)制寫(xiě)法是 7f0d0367,查看 app\build\intermediates\symbols\debug\R.txt

這里我們可以得知這個(gè) QiyiDraweeView 的 id 名稱(chēng)是 poster,那么可以搜索下整個(gè)項(xiàng)目
android:id="@+id/poster"
結(jié)果數(shù)太多,還是無(wú)法定位
我們看下 PinnedSectionListView,它引用鏈下的第一個(gè) View 很有可能是列表的 convertView,這里的話(huà)也就是 LinearLayout,找找看有沒(méi)有有用的信息

看到在 getView() 里 setTag() 設(shè)置的 GameAndAppCardModel.ViewHolder
原因
不合理的原因也很簡(jiǎn)單,就是布局里 ImageView 沒(méi)有指定寬高(固定多少 dp),所以在 Fresco setResizeOptions() 的時(shí)候,獲取不到該控件的寬高(項(xiàng)目中使用 Universal-Image-Loader 也一樣有這樣的問(wèn)題)。解決方法,見(jiàn)下篇