一谬运、Android內(nèi)存管理機(jī)制
1、Java對(duì)象的生命周期
Java對(duì)象的生命周期經(jīng)歷7個(gè)階段垦藏,分別是創(chuàng)建階段梆暖、應(yīng)用階段、不可見(jiàn)階段掂骏、不可達(dá)階段轰驳、收集階段、終結(jié)階段、對(duì)象控件重新分配階段滑废。
2蝗肪、內(nèi)存回收機(jī)制
內(nèi)存的三個(gè)區(qū)域
內(nèi)存會(huì)有三個(gè)區(qū)域,Yong Generation(年輕代)蠕趁、Old Generation(年老代)薛闪、permanent Generation(持久代)。
其中年輕代里面又分為三個(gè)區(qū)俺陋,eden豁延、S0、S1腊状。
內(nèi)存的處理過(guò)程:
1.對(duì)象創(chuàng)建后在Eden區(qū)域诱咏,
2.執(zhí)行GC時(shí),如果對(duì)象仍然存貨缴挖,則復(fù)制到S0區(qū)袋狞。
3.當(dāng)S0區(qū)滿(mǎn)時(shí),改區(qū)域存活對(duì)象將復(fù)制到S1區(qū)映屋,然后S0清空苟鸯,接下來(lái)S0和S1角色互換。
4.當(dāng)?shù)谌窟_(dá)到一定次數(shù)后棚点,存活對(duì)象將被復(fù)制到Old Generation早处。
5.當(dāng)這個(gè)對(duì)象在Old Generation區(qū)域挺溜的時(shí)間達(dá)到一定程度時(shí),它會(huì)被移動(dòng)到Old Generation瘫析,最后積累一定時(shí)間再移動(dòng)到Permaent Generation區(qū)域砌梆,Permaent Generation區(qū)域也存放一些靜態(tài)文件。
GC回收的一些算法
Copying算法:掃描出存活的對(duì)象贬循,并復(fù)制到一塊新的完全未使用的控件中咸包,對(duì)應(yīng)于Young Generation,就是在Eden甘有、FromSpace或ToSpace之間copy诉儒。
標(biāo)記算法:掃描出存活對(duì)象,然后再回收未標(biāo)記的對(duì)象亏掀,回收后對(duì)空出的空間要么合并,要么標(biāo)記出來(lái)便于下次分配泛释,以減少內(nèi)存碎片帶來(lái)的損耗滤愕。年老代對(duì)象存活時(shí)間較長(zhǎng)較穩(wěn)定,使用標(biāo)記算法回收怜校。
GC類(lèi)型
1.kGcCauseForAlloc:在分配內(nèi)存時(shí)內(nèi)存不夠情況下引起的GC,這種情況下GC會(huì)stop World间影。Stop World 是由于并發(fā)GC時(shí),其他線(xiàn)程都會(huì)停止茄茁。
2.kGcCauseBackground:當(dāng)內(nèi)存達(dá)到一定閾值的時(shí)候引發(fā)GC魂贬,這個(gè)時(shí)候是一個(gè)后臺(tái)GC,不會(huì)引起Stop World巩割。
3.kGcCauseExplicit:顯示調(diào)用時(shí)進(jìn)行的GC,如果ART打開(kāi)了這個(gè)選項(xiàng)付燥,在system.gc時(shí)會(huì)進(jìn)行GC宣谈。
其他GC注意事項(xiàng)
1.盡量不去顯式調(diào)用 system.gc() 減少不必要的系統(tǒng)開(kāi)銷(xiāo),影響應(yīng)用的流暢度键科。
2.盡量減少內(nèi)存泄露闻丑,避免OOM。
二勋颖、Android內(nèi)存泄露
1嗦嗡、什么是內(nèi)存泄露?
java對(duì)象有自己的生命周期饭玲,當(dāng)這個(gè)對(duì)象不需要再使用時(shí)侥祭,應(yīng)該完整地走完生命周期,但因?yàn)槟承┰蚯牙澹瑢?duì)象雖然已經(jīng)不再使用卑硫,仍然在內(nèi)存中并沒(méi)有結(jié)束整個(gè)生命周期,這就意味著這個(gè)對(duì)象已經(jīng)泄露了蚕断。
GC會(huì)選擇一些還存活的對(duì)象作為內(nèi)存遍歷的根節(jié)點(diǎn) GC Roots,通過(guò)對(duì)GC Roots的可達(dá)性來(lái)判斷是否需要回收欢伏。
Android系統(tǒng)虛擬機(jī)的垃圾回收是通過(guò)虛擬機(jī)GC機(jī)制來(lái)實(shí)現(xiàn)的。GC會(huì)選擇一些存活的對(duì)象作為內(nèi)存便利的跟節(jié)點(diǎn)GC Roots,通過(guò)判斷GC Roots的可達(dá)性來(lái)判斷是否需要回收亿乳,如上圖其中 1 2 3 4直接或間接被GC Roots引用鏈相連硝拧,這類(lèi)對(duì)象被認(rèn)為還需要使用的對(duì)象,就不會(huì)被回收葛假。5 6 7將會(huì)被回收障陶。到那時(shí)這里如果Object4 如果不需要使用的話(huà)這時(shí)候也不會(huì)被回收,就屬于內(nèi)存泄露聊训。
2抱究、常見(jiàn)內(nèi)存泄露場(chǎng)景以及注意事項(xiàng)
~ 資源型對(duì)象未關(guān)閉
~ 注冊(cè)對(duì)象未注銷(xiāo)
~ 類(lèi)的靜態(tài)變量持有大數(shù)據(jù)對(duì)象 如bitmap
~ 費(fèi)靜態(tài)內(nèi)部類(lèi)的靜態(tài)實(shí)例
~ Handler臨時(shí)性?xún)?nèi)存泄露
~ 容器中的對(duì)象沒(méi)有清理造成的內(nèi)存泄露
3、內(nèi)存泄露分析工具
leakcanary
三带斑、常見(jiàn)注意事項(xiàng)避免內(nèi)存消耗過(guò)多
1鼓寺、AutoBoxing自動(dòng)裝箱過(guò)程
Integer num=0;
for(int i=0;i<100;i++){
num+=i;
??}??
這段代碼每次循環(huán),虛擬機(jī)都必須創(chuàng)建一個(gè)新的整數(shù)對(duì)象勋磕,并把它加到其他整數(shù)對(duì)象前面妈候,創(chuàng)建一個(gè)新的整數(shù)對(duì)象,意味著要消耗更多性能挂滓。int只有4字節(jié)苦银,而Integer對(duì)象有16字節(jié)。
2、內(nèi)容復(fù)用
1幔虏、有效利用系統(tǒng)自帶資源纺念。
2、視圖復(fù)用想括,如ViewHolder陷谱。
3、對(duì)象池主胧。
4叭首、Bitmap對(duì)象復(fù)用。
3踪栋、使用最優(yōu)的數(shù)據(jù)類(lèi)型
1焙格、當(dāng)對(duì)象的數(shù)目在1000以?xún)?nèi)且特別多訪(fǎng)問(wèn)而刪除和插入不高的時(shí)候盡量用ArrayMap替代HashMap。
2夷都、枚舉的最大優(yōu)點(diǎn)是安全眷唉、易讀,但是內(nèi)存消耗是定義常量的三倍以上囤官《簦可以使用注解方式來(lái)檢查安全。
3党饮、使用IntDef和StringDef檢查類(lèi)型安全肝陪。
4、LruCache建議使用這個(gè)緩存機(jī)制刑顺,但是既不能分配太大氯窍,也不能分配太小。
4蹲堂、圖片的內(nèi)存優(yōu)化
設(shè)置位圖規(guī)格狼讨,使用inSampleSize實(shí)現(xiàn)位圖縮放和壓縮。使用緩存機(jī)制等柒竞。
四政供、內(nèi)存分析工具
1、Memory Monitor
這個(gè)是一個(gè)我們開(kāi)發(fā)過(guò)程中很常用的內(nèi)存朽基、CPU布隔、網(wǎng)絡(luò)的分析工具。
界面很直觀(guān)踩晶,左上角有運(yùn)行的機(jī)型和項(xiàng)目包名执泰,然后最直觀(guān)的動(dòng)態(tài)圖,分別是CPU渡蜻、Memory、NetWork。點(diǎn)進(jìn)去可以進(jìn)入的Memory茸苇。
這里可以清晰的看到顏色對(duì)應(yīng)區(qū)域占用的內(nèi)存大小排苍。
通過(guò)這兩張圖 內(nèi)存的大部分信息都能查閱到。我們?cè)诓僮鰽PP加載圖片等操作時(shí)候能看到內(nèi)存上升学密、和下降淘衙,如果操作APP后發(fā)現(xiàn)內(nèi)存不會(huì)下降可能就是我們有些對(duì)象沒(méi)有及時(shí)釋放,也有可能導(dǎo)致內(nèi)存泄露腻暮。
還有對(duì)應(yīng)的模擬GC彤守,查看時(shí)間段具體內(nèi)存信息以及一段時(shí)間的內(nèi)存追蹤等工具可以使用】蘧福可以定位到具體的代碼行具垫。如下圖:
這里有一個(gè)Shallow size這個(gè)屬性的概念:
Shallow size就是對(duì)象本身占用內(nèi)存的大小,不包含其引用的對(duì)象试幽。常規(guī)對(duì)象(非數(shù)組)的Shallow size有其成員變量的數(shù)量和類(lèi)型決定筝蚕。數(shù)組的shallow size有數(shù)組元素的類(lèi)型(對(duì)象類(lèi)型、基本類(lèi)型)和數(shù)組長(zhǎng)度決定铺坞。
2起宽、Heap Viewrer
如果是Android Studio的話(huà)通過(guò)Tools->Android->Android Device Monitor找到這個(gè)工具。
進(jìn)入后選擇運(yùn)行APP的包名然后點(diǎn)擊update Heap按鈕济榨,這時(shí)候會(huì)在每次gc時(shí)展示數(shù)據(jù)信息坯沪,也可以在后面手動(dòng)GC,如果在操作頁(yè)面這時(shí)候可能會(huì)發(fā)現(xiàn)小卡頓擒滑,因?yàn)樵贕C時(shí)腐晾,可能導(dǎo)致其他線(xiàn)程停止工作,這時(shí)可以清晰看到表中內(nèi)存信息:
頭部總覽視圖:
標(biāo)題 | 含義 |
---|---|
Heap Size | 堆棧分配給APP的內(nèi)存大小橘忱。 |
Allocated | 已分配使用的內(nèi)存大小赴魁。 |
Free | 空閑的內(nèi)存大小。 |
%Used | Allocated/Heap Size 的使用率钝诚。 |
#Object | 對(duì)象數(shù)量 |
下面詳情視圖:
標(biāo)題 | 含義 |
---|---|
free | 空閑的對(duì)象 |
data object | 數(shù)據(jù)對(duì)象颖御,Java類(lèi)類(lèi)型對(duì)象,是最主要的觀(guān)察對(duì)象凝颇。 |
class object | java類(lèi)類(lèi)型的引用對(duì)象潘拱。 |
1-byte array(byte[],boolean[]) | 一字節(jié)的數(shù)組對(duì)象。 |
2-byte array(short[],char[]) | 兩字節(jié)的數(shù)組對(duì)象拧略。 |
4-byte array(object[],int[],float[]) | 4字節(jié)的數(shù)組對(duì)象芦岂。 |
8-byte array(long[],double[]) | 8字節(jié)的數(shù)組對(duì)象。 |
non-java object | 非Java對(duì)象垫蛆。 |
每個(gè)類(lèi)型的數(shù)據(jù)值對(duì)應(yīng):
標(biāo)題 | 含義 |
---|---|
Count | 數(shù)量 |
Total Size | 總共占用的內(nèi)存的大小 |
Smallest | 將對(duì)象占用內(nèi)存從小到大排列禽最,排在第一個(gè)對(duì)象占用內(nèi)存大小 |
Largest | 將對(duì)象占用內(nèi)存從小到大排列腺怯,排在最后一個(gè)對(duì)象占用的內(nèi)存大小。 |
Median | 將對(duì)象占用內(nèi)存從小到大排列川无,排在總監(jiān)的對(duì)象占用的內(nèi)存大小呛占。 |
Average | 平均值 |