⑴ android Bitmap 內存以及OOM問題討論
都知道在Android中, 每個應用所使用的內存是有限的,現在的手機通常最大的內存使用為256M, 目前還沒發現Android中一個應用的最大內存分配超過256M的(經測試華為手機的最大內存是385M)
相關API:
ActivityManager.getMemoryClass(),首先獲取系統服務中的ActivityManager
如下:
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
可以獲取到相關信息
最近一直被項目的OOM問題困擾, 在網上查閱相關資料,前前後後讀了不下於30篇,這些篇幅講解的東西都是千篇一律,並沒有解決到實際問題
也在慕課網學習了內存優化章節
這是慕課網講師的PPT,我截屏的
這里來仔細分析一下:
第一個, 注意臨時Bitmap對象的及時回收, 來看下相關API
直接上圖
經過我無數次的使用Android studio工具自帶的MAT分析工具後, 得出一個很嚴謹的結論, 此方法並不奏效...
Android中Bitmap的內存存放在堆區, Google 的Bitmap的recycle方法注釋也可以了解到
Android歷史版本不是很清楚, 據說Android3.0之前Bitmap是存放在native區域,可以進行手動釋放,然而3.0之後Bitmap是存放在java層的堆區,沒錯是heap, 內存管理直接交由系統GC管理,你還這樣釋放資源有意義?無非是給自己的一點心理安慰罷了, 告訴你沒卵用
又有人在說要釋放內存使用System.gc() ??? 對就是主動觸發垃圾回收,這個API是開發者自行調用的嗎?那麼系統管理內存還有什麼意義?這不是誤人子弟嗎,這個API不能調用的, 因為沒卵用的, 具體自己參照MAT工具自行分析.即便垃圾回收真的被觸發了, 所有線程停滯由系統來清理垃圾, 造成的後果是嚴重卡頓!!!
再看一個API:
我在網上苦苦追尋內存過高的問題後,發現了這個API,經過無數次實踐後我得出一個結論,沒卵用...開發者可以拋棄它
綜上所訴, 第一點講解的內存優化問題可以直接PASS掉, 無非給自己一點心理安慰: 我已經處理好了內存問題, 程序不會OOM?
第二點. 避免Bitmap的浪費
直接說結論, 這個是非常行之有效的,並且是一定能解決問題的
具體怎麼操作呢? 自己實現LruCache這個類, 就是這么弄, 原理就是解碼復用, 在內存中已經解碼好的Bitmap直接拿出來使用, 沒有的在載入到內存進行解析, 這個非常有效,但是並不能讓你避免OOM
第三點, try-catch某些大內存分配的操作
這點上,我又要開始疑問了, 我Java功底不是很好
Java中發生內存溢出時,拋出的是OutOfMemoryError, 它的父類是VirtualMachineError
這玩意能catch住? 它屬於Error范疇, 你能捕獲?請Java大神出來說一下,我解釋不清楚
第四點, 載入Bitmap 縮放比例, 解碼格式, 局部載入
先來分析一下縮放比例:
按照市面上主流的手機解析度來分析現在Android主流的解析度是1920X1080, 如果一個ImageView控制項剛好就是屏幕全屏,怎麼說?直接佔用掉8M內存, 想想一個實際的需求情況
一個查看大圖的頁面, 不斷的關閉,打開查看新的大圖,問題就來了,內存一直在暴增,遲早會突破界限OOM掉
解析度是2K屏呢? 更恐怖了, 隨著手機設備的屏幕解析度提升, 載入圖像需要的內存也是倍增的, 因為應用的最大內存並沒有增加
結論: 縮放比例可以有效的降低內存佔用問題, 但不是絕對的救命稻草, 該縮放的還是要縮放,而且必須縮放,就是采樣 現在通常都是圖片載入框架來完成,類似Glide.Picasso,Fresco等,他們可以幫助你減少工作量, 內存問題還是存在
再來分析一下解碼格式:
這個跟縮放比例效果差不多, 只是同樣的解析度的圖片載入到內存中時佔用內存減少了
比如ARGB_8888 共32位
RGB_565 共16位
ARGB_4444 共16位
很明顯這樣格式圖片載入的內存情況是ARGB_8888是其他格式的兩倍內存
另外的問題是ARGB_8888看起來很清晰的, 其它的看起來圖片有種糊了的感覺,自己選擇吧
結論, 降低內存佔用非常有效
最後一個是局部載入, 並沒有怎麼使用到,也不清楚就不說了
最後還有一個方法避免OOM, 開啟largeHeap屬性, 但是但是, 以前我們開啟這個屬性後被Oppo應用市場認定為佔用內存過高, 不建議用戶安裝......所以我們又取消了!
總的來說, Bitmap在內存是變現的是不可控, 我項目OOM問題一直沒有得到有效解決,因為圖片編輯視頻編輯之類的功能佔用內存過高,繼續使用OOM是必然的, 跟IOS的同學交流了一番,他們說IOS的應用內存可以佔用到1個G以上, 輕松跑到500M是沒問題的, IOS的內存機制可以持續給內存使用, 具體我也不清楚,並且他們可以手動釋放內存?malloc, free這樣子?
如果大家有比較好的方案,還望留言交流互相幫助 [笑臉.gif]
補充: Android8.0開始Bitmap數據內存存在native層, 單個應用可用的內存顯著增長, 極大的降低了OOM的概率(2018年3月22日)
⑵ Android內存優化五:Bitmap優化
Android內存優化一:java垃圾回收機制
Android內存優化二:內存泄漏
Android內存優化三:內存泄漏檢測與監控
Android內存優化四:OOM
Android內存優化五:Bitmap優化
壓縮比:scale = (flaot) targetDensity / density
targetDensity : 設備屏幕像素密度dpi
density: 圖片對應的文件夾的像素密度dpi
1)、同一張圖片放在不同的資源目錄下,其解析度會有變化。
2)、Bitmap的解析度越高,其解析後的寬高越小,甚至小於原有的圖片(及縮放),從而內存也響應的減少。
3)、圖片不放置任何資源目錄時,其使用默認解析度mdpi:160。
4)、資源目錄解析度和屏幕解析度一致時,圖片尺寸不會縮放。
Bitmap放在資源目錄中的計算方式為:
主要通過編碼、采樣、復用、匿名共享區進行優化
由於ARGB_4444的畫質慘不忍睹,一般假如對圖片沒有透明度要求的話,可以改成RGB_565,相比ARGB_8888將節省一半的內存開銷
其中,A代表透明度;R代表紅色;G代表綠色;B代表藍色。
ALPHA_8 表示8位Alpha點陣圖,即A=8,一個像素點佔用1個位元組,它沒有顏色,只有透明度。
ARGB_4444 表示16位ARGB點陣圖,即A=4,R=4,G=4,B=4,一個像素點佔4+4+4+4=16位,2個位元組。
ARGB_8888 表示32位ARGB點陣圖,即A=8,R=8,G=8,B=8,一個像素點佔8+8+8+8=32位,4個位元組。
RGB_565 表示16位RGB點陣圖,即R=5,G=6,B=5,它沒有透明度,一個像素點佔5+6+5=16位,2個位元組。
bitmap的佔用內存,是以bitmap的寬高和每個像素佔用的位元組數決定的。
根據BitmapFactory 的采樣率進行壓縮 設置采樣率,不能小於1 假如是2 則寬為之前的1/2,高為之前的1/2,一共縮小1/4 以此類推
圖片復用指的是inBitmap這個屬性。
不使用這個屬性,你載入三張圖片,系統會給你分配三份內存空間,用於分別儲存這三張圖片
如果用了inBitmap這個屬性,載入三張圖片,這三張圖片會指向同一塊內存,而不用開辟三塊內存空間。
inBitmap的限制:
1、3.0-4.3
復用的圖片大小必須相同
編碼必須相同
2、4.4以上
復用的空間大於等於即可
編碼不必相同
3、不支持WebP
4、圖片復用,這個屬性必須設置為true;
options.inMutable = true;
Android 系統為了進程間共享數據開辟的一塊內存區域,由於這塊區域不受應用的Head的大小限制,相當於可以繞開oom,FaceBook的Fresco首次應用到實際中。
限制:5.0以後就限制了匿名共享內存的使用。
在SDK 11 -> 18之間,重用的bitmap大小必須是一致的,例如給inBitmap賦值的圖片大小為100-100,那麼新申請的bitmap必須也為100-100才能夠被重用。從SDK 19開始,新申請的bitmap大小必須小於或者等於已經賦值過的bitmap大小。 新申請的bitmap與舊的bitmap必須有相同的解碼格式,例如大家都是8888的,如果前面的bitmap是8888,那麼就不能支持4444與565格式的bitmap了。 我們可以創建一個包含多種典型可重用bitmap的對象池,這樣後續的bitmap創建都能夠找到合適的「模板」去進行重用。
8.0Bitmap的像素數據存儲在Native,為什麼又改為Native存儲呢?
因為8.0共享了整個系統的內存,測試8.0手機如果一直創建Bitmap,如果手機內存有1G,那麼你的應用載入1G也不會oom。
可以利用LRU開管理Bitmap,給他設置內存最大值,及時回收。
BitmapRegionDecoder
⑶ android 中 BitmapFactory 的內存怎樣回收
手敲原創,復制或轉載請註明。!
因為Android也是java,所以它的回收機制也是自動回收。
回收機制中,有 年輕代 和 老年代 的說法,年輕代就是尚在使用的內存對象或者在此之前不久都還在被使用的內存對象,老年代中就是所有已未使用或者被賦null的對象,所謂的自動回收,就是不斷的將不用的或為null的對象從年輕代中移至老年代中,並清除老年代中的所有對象。
所以--------->我們應當把我們不再用的對象主動的賦null,這樣就主動的交至老年代了,就被Android回收了。