android内存不足,Android OutOfMemoryError:内存不足问题的排查与解决
程序出現(xiàn)了內(nèi)存不足的問(wèn)題。經(jīng)過(guò)DDMS的監(jiān)控,發(fā)現(xiàn)作為一個(gè)apk才十來(lái)M的程序,竟然把系統(tǒng)最多分配的128M內(nèi)存占光光。經(jīng)過(guò)一系列曲折的過(guò)程,基本查明了所有的問(wèn)題所在。
內(nèi)存泄露
為了便于理解,這里把一個(gè)對(duì)象里引用另一個(gè)對(duì)象的現(xiàn)象,稱為“持有”;把垃圾回收器及其動(dòng)作,統(tǒng)稱為GC;如果A對(duì)象持有B對(duì)象,B對(duì)象又持有C對(duì)象,形成一個(gè)鏈條樣結(jié)構(gòu),稱為一個(gè)持有鏈。
GC的一個(gè)基本方法是“標(biāo)記-清掃”,就是從堆棧和靜態(tài)存儲(chǔ)區(qū)出發(fā),遍歷所有的對(duì)象和它們所持有的對(duì)象,形成一個(gè)樹(shù)形結(jié)構(gòu)(稱為引用樹(shù)),對(duì)于被遍歷到的對(duì)象進(jìn)行標(biāo)記,最后釋放掉那些沒(méi)有被標(biāo)記的對(duì)象。
堆棧和靜態(tài)存儲(chǔ)區(qū)里的對(duì)象,在這里通常被稱為GC Roots。根據(jù)這篇文章,靜態(tài)存儲(chǔ)區(qū)存保存的對(duì)象通常有以下幾類:
Class - 由系統(tǒng)類加載器(system class loader)加載的對(duì)象
Thread - 活著的線程
Stack Local - Java方法的local變量或參數(shù)
JNI Local - JNI方法的local變量或參數(shù)
JNI Global - 全局JNI引用
Monitor Used - 用于同步的監(jiān)控對(duì)象
Held by JVM - 用于JVM特殊目的由GC保留的對(duì)象,但實(shí)際上這個(gè)與JVM的實(shí)現(xiàn)是有關(guān)的。
如果一個(gè)對(duì)象,直接或者間接地被上面這些GC Roots所持有的話,即使已經(jīng)沒(méi)有用了,也不會(huì)被GC清理掉,內(nèi)存泄漏就這樣產(chǎn)生了。
這種不合理的持有鏈通常有以下這些類型:
Android特有的隱式持有
與Java不同地,Android中會(huì)發(fā)生一些隱式的持有。比如,在一個(gè)Activity里,通過(guò)調(diào)用findById()、getDrawable()等函數(shù)的方式,獲取到一個(gè)畫面元素,那么這個(gè)元素就會(huì)隱式地持有當(dāng)前的Activity作為mContext。一般情況下,這樣并沒(méi)有問(wèn)題;但如果這個(gè)元素是作為靜態(tài)對(duì)象定義在Activity的類中,就不會(huì)被釋放,從而導(dǎo)致Activity也不能釋放。
Android內(nèi)存泄漏排查工具:Leak Canary
這個(gè)工具引用到項(xiàng)目中后,在程序運(yùn)行過(guò)程中,如果出現(xiàn)了內(nèi)存泄露,就會(huì)自動(dòng)提示在手機(jī)屏幕上;點(diǎn)擊通知區(qū)域中的相應(yīng)條目,就可以看到泄露的Activity和相應(yīng)的持有鏈,對(duì)于分析內(nèi)存泄漏十分有效。
需要注意的是,因?yàn)樾枰訢ebugCompile的方式編譯到程序中去,所以這個(gè)庫(kù)只有在Gradle構(gòu)建的項(xiàng)目中才可以引用,基于Ant的項(xiàng)目就請(qǐng)先遷移到Gradle上去吧。
不合理的圖片-DPI對(duì)應(yīng)
現(xiàn)在手機(jī)屏幕的效果越來(lái)越細(xì)膩,除了顏色越來(lái)越鮮艷之外,像素密度——DPI也越來(lái)越高(所謂的視網(wǎng)膜屏)。Android為了正確處理不同DPI下圖片顯示的效果,規(guī)定了程序的圖片按不同的DPI設(shè)計(jì)多套,分別分別存放在各自的目錄下的方法。在程序運(yùn)行時(shí),由系統(tǒng)挑選最合適的目錄進(jìn)行顯示。
按DPI從小到大,這些目錄分別為: drawable-ldpi(低)→ drawable-mdpi(中)→ drawable-hdpi(高)→ drawable-xhdpi(超高)→ drawable-xxhdpi(超超高)→ ……
DPI并不等同于屏幕分辨率,也不等同于屏幕尺寸,它們之間的關(guān)系簡(jiǎn)單來(lái)說(shuō)就是: DPI×屏幕尺寸=分辨率。
但是由于這幾年的手機(jī)尺寸大部分都在5寸上下(需要兩只手抱著打電話的那種奇葩沒(méi)法說(shuō)),所以剩下的兩個(gè)變量,DPI和分辨率之間是有一個(gè)粗略的對(duì)應(yīng)關(guān)系的,如下表:
在設(shè)計(jì)圖片的時(shí)候,如果能按照上面這幾種尺寸分別設(shè)計(jì)好圖片,一般來(lái)說(shuō)在分辨率上就不會(huì)有太大的偏差。
以上內(nèi)容其實(shí)有點(diǎn)跑題了,那么圖片DPI和內(nèi)存占用之間有什么關(guān)系呢?
非常有關(guān)系!經(jīng)過(guò)dump heap,找到一些占用內(nèi)存比較兇狠(5M起)的BitMap對(duì)象,對(duì)其中mData對(duì)象進(jìn)行保存,并用 XXXXXXX打開(kāi)(顯示出來(lái)),找到這張圖片后大吃一驚:一個(gè)10KB左右的圖片,在內(nèi)存中竟然占到7MB!
上面這一系列操作的詳細(xì)方法點(diǎn)這里XXXXXXXXXX和這里XXXXXXXXX(待補(bǔ)充)。 順便吐槽下,IntelliJ比起Android Studio要先進(jìn)好幾個(gè)版本,但是只要程序稍稍多占用點(diǎn)內(nèi)存的時(shí)候,用IntelliJ dump heap完全不行,直接卡死,耽誤了好長(zhǎng)時(shí)間,最后換了Android才完成。
言歸正傳,為什么一張圖片會(huì)占用這么大的內(nèi)存空間?有些老舊的項(xiàng)目,最初不注意DPI的問(wèn)題,甚至就直接把所有的圖片放在drawable下。這種情況下,其實(shí)是當(dāng)作mdpi來(lái)處理的。但現(xiàn)在的手機(jī)屏幕,動(dòng)輒都是xhdpi起,和mdpi之間相差兩三個(gè)檔次。
總結(jié)
以上是生活随笔為你收集整理的android内存不足,Android OutOfMemoryError:内存不足问题的排查与解决的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: html5下拉列表默认值,element
- 下一篇: 控件不支持html5,javascrip