2019-01-16 Android內(nèi)存回收機(jī)制以及適配

1.垃圾收集算法的核心思想

Java語言建立了垃圾收集機(jī)制,用以跟蹤正在使用的對(duì)象和發(fā)現(xiàn)并回收不再使用(引用)的對(duì)象。該機(jī)制可以有效防范動(dòng)態(tài)內(nèi)存分配中因內(nèi)存垃圾過多而引發(fā)的內(nèi)存耗盡,以及不恰當(dāng)?shù)膬?nèi)存釋放所造成的內(nèi)存非法引用。

 垃圾收集算法的核心思想是:對(duì)虛擬機(jī)可用內(nèi)存空間,即堆空間中的對(duì)象進(jìn)行識(shí)別,如果對(duì)象正在被引用,那么稱其為存活對(duì)象,反之,如果對(duì)象不再被引用,則?為垃圾對(duì)象,可以回收其占據(jù)的空間,用于再分配。垃圾收集算法的選擇和垃圾收集系統(tǒng)參數(shù)的合理調(diào)節(jié)直接影響著系統(tǒng)性能,因此需要開發(fā)人員做比較深入的了解。

觸發(fā)主GC(Garbage?Collector)的條件

 JVM進(jìn)行次GC的頻率很高,但因?yàn)檫@種GC占用時(shí)間極短,所以對(duì)系統(tǒng)產(chǎn)生的影響不大。更值得關(guān)注的是主GC的觸發(fā)條件,因?yàn)樗鼘?duì)系統(tǒng)影響很明顯??偟膩碚f,有兩個(gè)條件會(huì)觸發(fā)主GC:

 ?、佼?dāng)應(yīng)用程序空閑時(shí),即沒有應(yīng)用線程在運(yùn)行時(shí),GC會(huì)被調(diào)用。因?yàn)镚C在優(yōu)先級(jí)最低的線程中進(jìn)行,所以當(dāng)應(yīng)用忙時(shí),GC線程就不會(huì)被調(diào)用,但以下條件除外。

  ②Java堆內(nèi)存不足時(shí),GC會(huì)被調(diào)用。當(dāng)應(yīng)用線程在運(yùn)行,并在運(yùn)行過程中創(chuàng)建新對(duì)象,若這時(shí)內(nèi)存空間不足,JVM就會(huì)強(qiáng)制地調(diào)用GC線程,以?便回收內(nèi)存用于新的分配。若GC一次之后仍不能滿足內(nèi)存分配的要求,JVM會(huì)再進(jìn)行兩次GC作進(jìn)一步的嘗試,若仍無法滿足要求,則?JVM將報(bào)“out?of?memory”的錯(cuò)誤,Java應(yīng)用將停止。

3.減少GC開銷的措施

根據(jù)上述GC的機(jī)制,程序的運(yùn)行會(huì)直接影響系統(tǒng)環(huán)境的變化,從而影響GC的觸發(fā)。若不針對(duì)GC的特點(diǎn)進(jìn)行設(shè)計(jì)和編碼,就會(huì)出現(xiàn)內(nèi)存駐留等一系列負(fù)面影響。為了避免這些影響,基本的原則就是盡可能地減少垃圾和減少GC過程中的開銷。具體措施包括以下幾個(gè)方面:

  (1)不要顯式調(diào)用System.gc()

  此函數(shù)建議JVM進(jìn)行主GC,雖然只是建議而非一定,但很多情況下它會(huì)觸發(fā)主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數(shù)。

  (2)盡量減少臨時(shí)對(duì)象的使用

  臨時(shí)對(duì)象在跳出函數(shù)調(diào)用后,會(huì)成為垃圾,少用臨時(shí)變量就相當(dāng)于減少了垃圾的產(chǎn)生,從而延長(zhǎng)了出現(xiàn)上述第二個(gè)觸發(fā)條件出現(xiàn)的時(shí)間,減少了主GC的機(jī)會(huì)。

  (3)對(duì)象不用時(shí)最好顯式置為Null

  一般而言,為Null的對(duì)象都會(huì)被作為垃圾處理,所以將不用的對(duì)象顯式地設(shè)為Null,有利于GC收集器判定垃圾,從而提高了GC的效率。

  (4)盡量使用StringBuffer,而不用String來累加字符串(詳見blog另一篇文章JAVA中String與StringBuffer)

  由于String是固定長(zhǎng)的字符串對(duì)象,累加String對(duì)象時(shí),并非在一個(gè)String對(duì)象中擴(kuò)增,而是重新創(chuàng)建新的String對(duì)象,如?Str5=Str1+Str2+Str3+Str4,這條語句執(zhí)行過程中會(huì)產(chǎn)生多個(gè)垃圾對(duì)象,因?yàn)閷?duì)次作“+”操作時(shí)都必須創(chuàng)建新的String對(duì)象,但?這些過渡對(duì)象對(duì)系統(tǒng)來說是沒有實(shí)際意義的,只會(huì)增加更多的垃圾。避免這種情況可以改用StringBuffer來累加字符串,因StringBuffer?是可變長(zhǎng)的,它在原有基礎(chǔ)上進(jìn)行擴(kuò)增,不會(huì)產(chǎn)生中間對(duì)象。

  (5)能用基本類型如Int,Long,就不用Integer,Long對(duì)象

  基本類型變量占用的內(nèi)存資源比相應(yīng)對(duì)象占用的少得多,如果沒有必要,最好使用基本變量。

  (6)盡量少用靜態(tài)對(duì)象變量

  靜態(tài)變量屬于全局變量,不會(huì)被GC回收,它們會(huì)一直占用內(nèi)存。

  (7)分散對(duì)象創(chuàng)建或刪除的時(shí)間

  集中在短時(shí)間內(nèi)大量創(chuàng)建新對(duì)象,特別是大對(duì)象,會(huì)導(dǎo)致突然需要大量?jī)?nèi)存,JVM在面臨這種情況時(shí),只能進(jìn)行主GC,以回收內(nèi)存或整合內(nèi)存碎片,?從而增加主GC的頻率。集中刪除對(duì)象,道理也是一樣的。它使得突然出現(xiàn)了大量的垃圾對(duì)象,空閑空間必然減少,從而大大增加了下一次創(chuàng)建新對(duì)象時(shí)強(qiáng)制主GC?的機(jī)會(huì)。



gc()函數(shù)的作用只是提醒虛擬機(jī):程序員希望進(jìn)行一次垃圾回收。但是它不能保證垃圾回收一定會(huì)進(jìn)行,而且具體什么時(shí)候進(jìn)行是取決于具體的虛擬機(jī)的,不同的虛擬機(jī)有不同的對(duì)策。在Davilk中,給程序分配的內(nèi)存是根據(jù)機(jī)型廠商的不同而不同(現(xiàn)在大部分為32MB),在VM內(nèi)部會(huì)將內(nèi)存分為:java使用的內(nèi)存,Native使用的內(nèi)存,他們之間不能共享,當(dāng)某一方面不足

的時(shí)候必須向VM申請(qǐng),而不能直接使用另外一個(gè)的內(nèi)存。

出現(xiàn)內(nèi)存泄漏的可能性:

出現(xiàn)情況:

1.?數(shù)據(jù)庫的cursor沒有關(guān)閉

2.構(gòu)造adapter時(shí),沒有使用緩存contentview

衍生listview的優(yōu)化問題-----減少創(chuàng)建view的對(duì)象,充分使用contentview,可以使用一靜態(tài)類來優(yōu)化處理getview的過程

3.Bitmap對(duì)象不使用時(shí)采用recycle()釋放內(nèi)存

4.activity中的對(duì)象的生命周期大于activity

調(diào)試方法:?DDMS==>?HEAPSZIE==>dataobject==>[Total?Size]



Android?內(nèi)存淺析【管理、機(jī)制、分析】

一、?Android的內(nèi)存機(jī)制

Android的程序由Java語言編寫,所以Android的內(nèi)存管理與Java的內(nèi)存管理相似。程序員通過new為對(duì)象分配內(nèi)存,所有對(duì)象在java?堆內(nèi)分配空間;然而對(duì)象的釋放是由垃圾回收器來完成的。C/C++中的內(nèi)存機(jī)制是“誰污染,誰治理”,java的就比較人性化了,給我們請(qǐng)了一個(gè)專門的清?潔工(GC)

二、GC是什么??為什么要有GC?

GC是垃圾收集的意思(Gabage?Collection),內(nèi)存處理是編程人員容易出現(xiàn)問題的地方,忘記或者錯(cuò)誤的內(nèi)存回收會(huì)導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰,Java提供的GC功能可以?自動(dòng)監(jiān)測(cè)對(duì)象是否超過作用域從而達(dá)到自動(dòng)回收內(nèi)存的目的,Java語言沒有提供釋放已分配內(nèi)存的顯示操作方法。


垃圾回收器的基本原理是什么?垃圾回收器可以馬上回收內(nèi)存嗎?有什么辦法主動(dòng)通知虛擬機(jī)進(jìn)行垃圾回收?

對(duì)于GC來說,當(dāng)程序員創(chuàng)建對(duì)象時(shí),GC就開始監(jiān)控這個(gè)對(duì)象的地址、大小以及使用情況。通常,GC采用有向圖的方式記錄和管理堆(heap)中的所有對(duì)?象。通過這種方式確定哪些對(duì)象是"可達(dá)的",哪些對(duì)象是"不可達(dá)的"。當(dāng)GC確定一些對(duì)象為"不可達(dá)"時(shí),GC就有責(zé)任回收這些內(nèi)存空間??梢?。程序員可?以手動(dòng)執(zhí)行System.gc(),通知GC運(yùn)行,但是Java語言規(guī)范并不保證GC一定會(huì)執(zhí)行。


間而忘記了釋放。如果程序中存在對(duì)無用對(duì)象的引用,那么這些對(duì)象就會(huì)駐留內(nèi)存,消耗內(nèi)存,因?yàn)闊o法讓垃圾回收器GC驗(yàn)證這些對(duì)象是否不再需要。如果存在對(duì)?象的引用,這個(gè)對(duì)象就被定義為"有效的活動(dòng)",同時(shí)不會(huì)被釋放。要確定對(duì)象所占內(nèi)存將被回收,我們就要?jiǎng)?wù)必確認(rèn)該對(duì)象不再會(huì)被使用。典型的做法就是把對(duì)象?數(shù)據(jù)成員設(shè)為null或者從集合中移除該對(duì)象。但當(dāng)局部變量不需要時(shí),不需明顯的設(shè)為null,因?yàn)橐粋€(gè)方法執(zhí)行完畢時(shí),這些引用會(huì)自動(dòng)被清理。

Java帶垃圾回收的機(jī)制,為什么還會(huì)內(nèi)存泄露呢?舉例:

[java]view?plaincopyprint?

Vector?v?=?new?Vector(10);??

????????for?(int?i?=?1;?i?<?100;?i++)?{??

????????????Object?o?=?new?Object();??

????????????v.add(o);??

????????????o?=?null;??

????????}//?此時(shí),所有的Object對(duì)象都沒有被釋放,因?yàn)樽兞縱引用這些對(duì)象。??

Java?內(nèi)存泄露的根本原因就是?保存了不可能再被訪問的變量類型的引用

六、Android的內(nèi)存溢出

Android的內(nèi)存溢出是如何發(fā)生的?

Android的虛擬機(jī)是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的機(jī)器為24M。也就是說我們所能利用的內(nèi)存空間是有限的。如果我們的內(nèi)存占用超過了一定的水平就會(huì)出現(xiàn)OutOfMemory的錯(cuò)誤。

為什么會(huì)出現(xiàn)內(nèi)存不夠用的情況呢?我想原因主要有兩個(gè):

由于我們程序的失誤,長(zhǎng)期保持某些資源(如Context)的引用,造成內(nèi)存泄露,資源造成得不到釋放。保存了多個(gè)耗用內(nèi)存過大的對(duì)象(如Bitmap),造成內(nèi)存超出限制。



在Android適配方案小結(jié)

、(二)中,我們了解了一些基本概念。

600dp的含義是:代表這個(gè)設(shè)備的最短的那一邊。

獲取設(shè)備的最短邊的代碼是:Configuration?config?=?getResources().getConfiguration();

int?smallestScreenWidth?=?config.smallestScreenWidthDp;

這個(gè)時(shí)候拿smallestScreenWidth?與600想比較就可以知道該設(shè)備能否讀取里面的資源了。

)?


除此之外,為了方便適配,在編碼時(shí)我們還應(yīng)該注意什么呢,主要有以下幾點(diǎn):

(1)多使用權(quán)重(android:layout_weight)

尤其是在tab切換布局,listview?title及Item布局等情況下;

(2)設(shè)置寬度和高度時(shí),盡量使用match_parent和wrap_content,避免把控件寬高設(shè)死;

(3)父容器布局選用

多使用RelativeLayout,F(xiàn)rameLayout,GridLayout等,減少布局層次。當(dāng)然,在使用

權(quán)重時(shí),得采用LinearLayout;

(4)?在xml里,設(shè)置高度、寬度采用dp(dip),設(shè)置字體采用sp。

(應(yīng)該注意,在代碼里面,我們寫的setHeight(...)單位是px)

那么在具體開發(fā)中,我們應(yīng)該注意什么呢。

首先,我們必須要知道,其實(shí)適配的關(guān)鍵在于兩點(diǎn):

(1)不同分辨率設(shè)備的適配,這點(diǎn)在單位的使用上用dp、sp以及圖片資源存放于不同的drawable文件夾就可以解決問題;

(2)不同尺寸的適配,這點(diǎn)主要靠將相關(guān)值以及布局文件放置于不同的文件夾中來解決。

2.1?values文件夾

可以在工程下創(chuàng)建不同的values文件夾:values-sw480dp,?values-sw600dp,

values-sw720dp-land等。比如一個(gè)控件的寬度,在10寸pad上是10dp,在8寸pad

上是5dp。這時(shí),你可以定義一個(gè)變量,button_width,然后在values-sw600dp

下寫5dp,在values-sw720-land下寫

10dp。這樣就達(dá)到了在不同尺寸pad上,

相應(yīng)控件大小不一樣的效果。

2.1?layout文件夾

如果在不同尺寸設(shè)備上展示的布局有明顯差別,僅僅用values不同已經(jīng)難以控制,

那么就可以考慮寫不同的布局文件置于不同的layout文件夾下,android會(huì)根據(jù)設(shè)備

尺寸去加載相應(yīng)文件夾下的布局文件。如:layout-sw480dp,layout-sw600dp,

layout-sw700dp等。

值得注意的是,如果不是很有必要,盡量采用2.1方案,方便維護(hù)。如果尺寸和分辨率都不同,

那么就要結(jié)合(1)、(2)考慮了。

(補(bǔ)充:其實(shí)values文件夾和layout文件夾不僅僅是根據(jù)尺寸判斷,也和分辨率有關(guān),不過在通常情況下,

綜合計(jì)算考慮,僅根據(jù)尺寸判斷就可以了:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容