Java垃圾回收器(GC):
在C,C++或其他程序設(shè)計語言中,資源或內(nèi)存都必須由程序員自行聲明產(chǎn)生和回收朝巫,否則其中的資源將消耗妥箕,造成資源的浪費甚至崩潰滥酥。但手工回收內(nèi)存往往是一項復(fù)雜而艱巨的工作。
于是畦幢,Java技術(shù)提供了一個系統(tǒng)級的線程坎吻,即垃圾收集器線程(Garbage Collection Thread),來跟蹤每一塊分配出去的內(nèi)存空間宇葱,當(dāng)Java 虛擬機(Java Virtual Machine)處于空閑循環(huán)
時瘦真,垃圾收集器線程會自動檢查每一快分配出去的內(nèi)存空間,然后自動回收每一快可以回收的無用的內(nèi)存塊黍瞧。
GC垃圾回收作用:
1.清除不用的對象來釋放內(nèi)存:
采用一種動態(tài)存儲管理技術(shù)诸尽,它自動地釋放不再被程序引用的對象,按照特定的垃圾收集算法來實現(xiàn)資源自動回收的功能印颤。當(dāng)一個對象不再被引用的時候您机,內(nèi)存回收它占領(lǐng)的空間,以便空間
被后來的新對象使用年局。
2.消除堆內(nèi)存空間的碎片:
由于創(chuàng)建對象和垃圾收集器釋放丟棄對象所占的內(nèi)存空間际看,內(nèi)存會出現(xiàn)碎片。碎片是分配給對象的內(nèi)存塊之間的空閑內(nèi)存洞矢否。碎片整理將所占用的堆內(nèi)存移到堆的一端仲闽,JVM將整理出的內(nèi)存分
配給新的對象。
垃圾回收器優(yōu)點:
1.減輕編程的負(fù)擔(dān)僵朗,提高效率:
使程序員從手工回收內(nèi)存空間的繁重工作中解脫了出來蔼囊,因為在沒有垃圾收集機制的時候,可能要花許多時間來解決一個難懂的存儲器問題衣迷。在用Java語言編程的時候畏鼓,靠垃圾收集機制可大
大縮短時間。
2.它保護程序的完整性:
因此垃圾收集是Java語言安全性策略的一個重要部份壶谒。
垃圾回收器缺點:
1.占用資源時間:
Java虛擬機必須追蹤運行程序中有用的對象, 而且最終釋放沒用的對象云矫。這一個過程需要花費處理器的時間。
2.不可預(yù)知:
垃圾收集器線程雖然是作為低優(yōu)先級的線程運行汗菜,但在系統(tǒng)可用內(nèi)存量過低的時候让禀,它可能會突發(fā)地執(zhí)行來挽救內(nèi)存資源。當(dāng)然其執(zhí)行與否也是不可預(yù)知的陨界。
3.不確定性:
不能保證一個無用的對象一定會被垃圾收集器收集巡揍,也不能保證垃圾收集器在一段Java語言代碼中一定會執(zhí)行。
同樣也沒有辦法預(yù)知在一組均符合垃圾收集器收集標(biāo)準(zhǔn)的對象中菌瘪,哪一個會被首先收集腮敌。
4.不可操作
垃圾收集器不可以被強制執(zhí)行阱当,但程序員可以通過調(diào)用System. gc方法來建議執(zhí)行垃圾收集器。
垃圾回收算法:
1.引用計數(shù)(Reference Counting)
比較古老的回收算法糜工。原理是此對象有一個引用弊添,即增加一個計數(shù),刪除一個引用則減少一個計數(shù)捌木。垃圾回收時油坝,只用收集計數(shù)為0的對象。此算法最致命的是無法處理循環(huán)引用的問題刨裆。
2.標(biāo)記-清除(Mark-Sweep)
此算法執(zhí)行分兩階段澈圈。第一階段從引用根節(jié)點開始標(biāo)記所有被引用的對象,第二階段遍歷整個堆帆啃,把未標(biāo)記的對象清除极舔。此算法需要暫停整個應(yīng)用,同時链瓦,會產(chǎn)生內(nèi)存碎片。
3.復(fù)制(Copying)
此算法把內(nèi)存空間劃為兩個相等的區(qū)域盯桦,每次只使用其中一個區(qū)域慈俯。垃圾回收時,遍歷當(dāng)前使用區(qū)域拥峦,把正在使用中的對象復(fù)制到另外一個區(qū)域中贴膘。次算法每次只處理正在使用中的對象,因
此復(fù)制成本比較小略号,同時復(fù)制過去以后還能進行相應(yīng)的內(nèi)存整理刑峡,不過出現(xiàn)“碎片”問題。當(dāng)然玄柠,此算法的缺點也是很明顯的突梦,就是需要兩倍內(nèi)存空間。
4.標(biāo)記-整理(Mark-Compact)
此算法結(jié)合了 “標(biāo)記-清除”和“復(fù)制”兩個算法的優(yōu)點羽利。也是分兩階段宫患,第一階段從根節(jié)點開始標(biāo)記所有被引用對象,第二階段遍歷整個堆这弧,把清除未標(biāo)記對象并且把存活對象 “壓縮”到
堆的其中一塊娃闲,按順序排放。此算法避免了“標(biāo)記-清除”的碎片問題匾浪,同時也避免了“復(fù)制”算法的空間問題皇帮。
5.增量收集(Incremental Collecting)
實施垃圾回收算法,即:在應(yīng)用進行的同時進行垃圾回收蛋辈。不知道什么原因JDK5.0中的收集器沒有使用這種算法的属拾。
6.分代(Generational Collecting)
基于對對象生命周期分析后得出的垃圾回收算法。把對象分為年青代、年老代捌年、持久代瓢娜,對不同生命周期的對象使用不同的算法(上述方式中的一個)進行回收。現(xiàn)在的垃圾回收器(從
J2SE1.2開始)都是使用此算法的礼预。
即將垃圾回收生命周期方法finalize():
每一個對象都有一個finalize方法眠砾,這個方法是從Object類繼承來的。
當(dāng)垃圾回收確定不存在對該對象的更多引用時托酸,由對象的垃圾回收器調(diào)用此方法褒颈。
Java 技術(shù)允許使用finalize方法在垃圾收集器將對象從內(nèi)存中清除出去之前做必要的清理工作。一旦垃圾回收器準(zhǔn)備好釋放對象占用的空間励堡,將首先調(diào)用其finalize()方法谷丸,并且在下一次垃
圾回收動作發(fā)生時,才會真正回收對象占用的內(nèi)存应结。
簡單的說finalize方法是在垃圾收集器刪除對象之前對這個對象調(diào)用的刨疼。
System.gc():
我們可以調(diào)用System.gc方法,建議虛擬機進行垃圾回收工作(注意鹅龄,是建議揩慕,但虛擬機會不會這樣干,我們也無法預(yù)知0缧荨)
下面來看一個例子來了解finalize()和System.gc()的使用:
public class TestGC {
public TestGC() {}
//當(dāng)垃圾回收器確定不存在對該對象的更多引用時迎卤,由對象的垃圾回收器調(diào)用此方法。
protected void finalize() {
System.out.println("我已經(jīng)被垃圾回收器回收了...");
}
public static void main(String [] args) {
TestGC gc = new TestGC();
gc = null;
// 建議虛擬機進行垃圾回收工作
System.gc();
}
}
如上面的例子所示玷坠,大家可以猜猜重寫的finalize方法會不會執(zhí)行蜗搔?
答案是:不一定!
因為無論是設(shè)置gc的引用為null還是調(diào)用System.gc()方法都只是"建議"垃圾回收器進行垃圾回收八堡,但是最終所有權(quán)還在垃圾回收器手中樟凄,它會不會進行回收我們無法預(yù)知!
垃圾回收面試題:
題目一:
1.fobj = new Object ( ) ;
2.fobj. Method ( ) ;
3.fobj = new Object ( ) ;
4.fobj. Method ( ) ;
問:這段代碼中兄渺,第幾行的fobj 符合垃圾收集器的收集標(biāo)準(zhǔn)不同?
答:第3行。因為第3行的fobj被賦了新值溶耘,產(chǎn)生了一個新的對象二拐,即換了一塊新的內(nèi)存空間,也相當(dāng)于為第1行中的fobj賦了null值凳兵。這種類型的題是最簡單的百新。
題目二:
1.Object sobj = new Object ( ) ;
2.Object sobj = null ;
3.Object sobj = new Object ( ) ;
4.sobj = new Object ( ) ;
問:這段代碼中,第幾行的內(nèi)存空間符合垃圾收集器的收集標(biāo)準(zhǔn)庐扫?
答:第2行和第4行饭望。因為第2行為sobj賦值為null仗哨,所以在此第1行的sobj符合垃圾收集器的收集標(biāo)準(zhǔn)。而第4行相當(dāng)于為sobj賦值為null铅辞,所以在此第3行的sobj也符合垃圾收集器的收集標(biāo)準(zhǔn)
厌漂。
如果有一個對象的句柄a,且你把a作為某個構(gòu)造器的參數(shù)斟珊,即 new Constructor ( a )的時候苇倡,即使你給a賦值為null,a也不符合垃圾收集器的收集標(biāo)準(zhǔn)囤踩。直到由上面構(gòu)造器構(gòu)造的新對象被
賦空值時旨椒,a才可以被垃圾收集器收集。
題目三:
1.Object aobj = new Object ( ) ;
2.Object bobj = new Object ( ) ;
3.Object cobj = new Object ( ) ;
4.a(chǎn)obj = bobj;
5.a(chǎn)obj = cobj;
6.cobj = null;
7.a(chǎn)obj = null;
問:這段代碼中堵漱,第幾行的內(nèi)存空間符合垃圾收集器的收集標(biāo)準(zhǔn)综慎?
答:第4,7行勤庐。注意這類題型是認(rèn)證考試中可能遇到的最難題型了示惊。
行1-3:分別創(chuàng)建了Object類的三個對象:aobj,bobj愉镰,cobj
行4:此時對象aobj的句柄指向bobj米罚,原來aojb指向的對象已經(jīng)沒有任何引用或變量指向,這時岛杀,就符合回收標(biāo)準(zhǔn)。
行5:此時對象aobj的句柄指向cobj崭孤,所以該行的執(zhí)行不能使aobj符合垃圾收集器的收集標(biāo)準(zhǔn)类嗤。
行6:此時仍沒有任何一個對象符合垃圾收集器的收集標(biāo)準(zhǔn)。
行7:對象cobj符合了垃圾收集器的收集標(biāo)準(zhǔn)辨宠,因為cobj的句柄指向單一的地址空間遗锣。在第6行的時候,cobj已經(jīng)被賦值為null嗤形,但由cobj同時還指向了aobj(第5行)精偿,所以此時cobj并不符合
垃圾收集器的收集標(biāo)準(zhǔn)。而在第7行赋兵,aobj所指向的地址空間也被賦予了空值null笔咽,這就說明了,由cobj所指向的地址空間已經(jīng)被完全地賦予了空值霹期。所以此時cobj最終符合了垃圾收集器的收
集標(biāo)準(zhǔn)叶组。 但對于aobj和bobj,仍然無法判斷其是否符合收集標(biāo)準(zhǔn)历造。
總之甩十,在Java語言中船庇,判斷一塊內(nèi)存空間是否符合垃圾收集器收集的標(biāo)準(zhǔn)只有兩個:
1.給對象賦予了空值null,以下再沒有調(diào)用過侣监。
2.給對象賦予了新值鸭轮,既重新分配了內(nèi)存空間。
最后再次提醒一下橄霉,一塊內(nèi)存空間符合了垃圾收集器的收集標(biāo)準(zhǔn)窃爷,并不意味著這塊內(nèi)存空間就一定會被垃圾收集器收集。
Thread(線程)回收:
線程中涉及的任何東西GC都不能回收(Anything reachable by a thread cannot be GC'd )酪劫,所以線程很容易造成內(nèi)存泄露吞鸭。
如下面代碼所示:
Thread t = new Thread() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
System.out.println("thread is running...");
} catch (InterruptedException e) {
}
}
}
};
t.start();
t = null;
System.gc();
如上在線程t中每間隔一秒輸出一段話,然后將線程設(shè)置為null并且調(diào)用System.gc方法覆糟。
最后的結(jié)果是線程并不會被回收刻剥,它會一直的運行下去。
因為運行中的線程是稱之為垃圾回收根(GC Roots)對象的一種滩字,不會被垃圾回收造虏。當(dāng)垃圾回收器判斷一個對象是否可達,總是使用垃圾回收根對象作為參考點麦箍。
如果線程是內(nèi)部類漓藕,這種用法默認(rèn)會持有當(dāng)前類對象不釋放。
Cursor(游標(biāo))回收:
Cursor是Android查詢數(shù)據(jù)后得到的一個管理數(shù)據(jù)集合的類挟裂,在使用結(jié)束以后享钞。應(yīng)該保證Cursor占用的內(nèi)存被及時的釋放掉,而不是等待GC來處理诀蓉。并且Android明顯是傾向于編程者手動的將
Cursor close掉栗竖,因為在源代碼中我們發(fā)現(xiàn),如果等到垃圾回收器來回收時渠啤,會給用戶以錯誤提示狐肢。
所以我們使用Cursor的方式一般如下:
Cursor cursor = null;
try {
cursor = mContext.getContentResolver().query(uri,null, null,null,null);
if(cursor != null) {
cursor.moveToFirst();
//do something
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
有一種情況下,我們不能直接將Cursor關(guān)閉掉沥曹,這就是在CursorAdapter中應(yīng)用的情況份名,但是注意,CursorAdapter在Acivity結(jié)束時并沒有自動的將Cursor關(guān)閉掉妓美,因此僵腺,你需要在onDestroy
函數(shù)中,手動關(guān)閉壶栋。
@Override
protected void onDestroy() {
if (mAdapter != null && mAdapter.getCurosr() != null) {
mAdapter.getCursor().close();
}
super.onDestroy();
}
Receiver(接收器)回收:
調(diào)用registerReceiver()后未調(diào)用unregisterReceiver().
當(dāng)我們Activity中使用了registerReceiver()方法注冊了BroadcastReceiver想邦,一定要在Activity的生命周期內(nèi)調(diào)用unregisterReceiver()方法取消注冊
也就是說registerReceiver()和unregisterReceiver()方法一定要成對出現(xiàn),通常我們可以重寫Activity的onDestory()方法:
@Override
protected void onDestroy() {
this.unregisterReceiver(receiver);
super.onDestroy();
}
Stream/File(流/文件)回收:
主要針對各種流委刘,文件資源等等如:
InputStream/OutputStream丧没,SQLiteOpenHelper鹰椒,SQLiteDatabase,Cursor呕童,文件漆际,I/O,Bitmap圖片等操作等都應(yīng)該記得顯示關(guān)閉夺饲。
和之前介紹的Cursor道理類似奸汇,就不多說了。
布局優(yōu)化:
減少視圖層級hierarchyviewer:
減少視圖層級可以有效的減少內(nèi)存消耗往声,因為視圖是一個樹形結(jié)構(gòu)擂找,每次刷新和渲染都會遍歷一次。
想要減少視圖層級首先就需要知道視圖層級浩销,所以下面介紹一個SDK中自帶的一個非常好用的工具hierarchyviewer贯涎。
你可以在下面的地址找到它:your sdk path\sdk\tools。
hierarchyviewer可以非常清楚的看到當(dāng)前視圖的層級結(jié)構(gòu)慢洋,并且可以查看視圖的執(zhí)行效率(視圖上的小圓點塘雳,綠色表示流暢,黃色和紅色次之)普筹,所以我們可以很方便的查看哪些view可能會
影響我們的性能從而去進一步優(yōu)化它败明。
hierarchyviewer還提供另外一種列表式的查看方式,可以查看詳細(xì)的屏幕畫面太防,具體到像素級別的問題都可以通過它發(fā)現(xiàn)妻顶。
ViewStub標(biāo)簽
此標(biāo)簽可以使UI在特殊情況下,直觀效果類似于設(shè)置View的不可見性蜒车,但是其更大的意義在于被這個標(biāo)簽所包裹的Views在默認(rèn)狀態(tài)下不會占用任何內(nèi)存空間讳嘱。
include標(biāo)簽
可以通過這個標(biāo)簽直接加載外部的xml到當(dāng)前結(jié)構(gòu)中,是復(fù)用UI資源的常用標(biāo)簽醇王。
merge標(biāo)簽
它在優(yōu)化UI結(jié)構(gòu)時起到很重要的作用呢燥。目的是通過刪減多余或者額外的層級崭添,從而優(yōu)化整個Android Layout的結(jié)構(gòu)寓娩。
布局用Java代碼比寫在XML中快 ,特別需要性能地方可以考慮:
一般情況下對于Android程序布局往往使用XML文件來編寫呼渣,這樣可以提高開發(fā)效率棘伴,但是考慮到代碼的安全性以及執(zhí)行效率,可以通過Java代碼執(zhí)行創(chuàng)建屁置,雖然Android編譯過的XML是二進制
的焊夸,但是加載XML解析器的效率對于資源占用還是比較大的,Java處理效率比XML快得多蓝角,但是對于一個復(fù)雜界面的編寫阱穗,可能需要一些套嵌考慮饭冬,如果你思維靈活的話,使用Java代碼來布局
你的Android應(yīng)用程序是一個更好的方法揪阶。
重用系統(tǒng)資源:
- 利用系統(tǒng)定義的id
比如我們有一個定義ListView的xml文件昌抠,一般的,我們會寫類似下面的代碼片段鲁僚。
<ListView
android:id="@+id/mylist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
這里我們定義了一個ListView炊苫,定義它的id是"@+id/mylist"。實際上冰沙,如果沒有特別的需求侨艾,就可以利用系統(tǒng)定義的id,類似下面的樣子拓挥。
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
在xml文件中引用系統(tǒng)的id唠梨,只需要加上“@android:”前綴即可。如果是在Java代碼中使用系統(tǒng)資源撞叽,和使用自己的資源基本上是一樣的姻成。不同的是,需要使用android.R類來使用系統(tǒng)的資源
愿棋,而不是使用應(yīng)用程序指定的R類科展。這里如果要獲取ListView可以使用android.R.id.list來獲取。
利用系統(tǒng)的圖片資源
這樣做的好處糠雨,一個是美工不需要重復(fù)的做一份已有的圖片了才睹,可以節(jié)約不少工時;另一個是能保證我們的應(yīng)用程序的風(fēng)格與系統(tǒng)一致甘邀。利用系統(tǒng)的字符串資源
如果使用系統(tǒng)的字符串琅攘,默認(rèn)就已經(jīng)支持多語言環(huán)境了。如上述代碼松邪,直接使用了@android:string/yes和@android:string/no坞琴,在簡體中文環(huán)境下會顯示“確定”和“取消”,在英文環(huán)境下
會顯示“OK”和“Cancel”逗抑。
- 利用系統(tǒng)的Style
假設(shè)布局文件中有一個TextView剧辐,用來顯示窗口的標(biāo)題,使用中等大小字體邮府∮兀可以使用下面的代碼片段來定義TextView的Style。
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" />
其中android:textAppearance="?android:attr/textAppearanceMedium"就是使用系統(tǒng)的style褂傀。需要注意的是忍啤,使用系統(tǒng)的style,需要在想要使用的資源前面加“?android:”作為前綴仙辟,而
不是“@android:”同波。
- 利用系統(tǒng)的顏色定義
除了上述的各種系統(tǒng)資源以外鳄梅,還可以使用系統(tǒng)定義好的顏色。在項目中最常用的未檩,就是透明色的使用卫枝。
android:background ="@android:color/transparent"
除了上面介紹的以外還有很多其他Android系統(tǒng)本身自帶的資源,它們在應(yīng)用中都可以直接使用讹挎。具體的校赤,可以進入android-sdk的相應(yīng)文件夾中去查看。例如:可以進入$android-sdk$
\platforms\android-8\data\res筒溃,里面的系統(tǒng)資源就一覽無余了马篮。
開發(fā)者需要花一些時間去熟悉這些資源,特別是圖片資源和各種Style資源怜奖,這樣在開發(fā)過程中浑测,能重用的盡量重用,而且有時候使用系統(tǒng)提供的效果可能會更好歪玲。
其他小tips:
- 分辨率適配-ldpi,-mdpi, -hdpi配置不同精度資源迁央,系統(tǒng)會根據(jù)設(shè)備自適應(yīng),包括drawable, layout,style等不同資源滥崩。
2.盡量使用dp(density independent pixel)開發(fā)岖圈,不用px(pixel)。
3.多用wrap_content, match_parent
4.永遠不要使用AbsoluteLayout
5.使用9patch(通過~/tools/draw9patch.bat啟動應(yīng)用程序)钙皮,png格式
6.將Acitivity中的Window的背景圖設(shè)置為空蜂科。getWindow().setBackgroundDrawable(null);android的默認(rèn)背景是不是為空。
7.View中設(shè)置緩存屬性.setDrawingCache為true短条。
框架設(shè)計:
是否定義了自己的Activity和fragment等常用控件的基類去避免進行重復(fù)的工作
是否有完善的異常處理機制导匣,即使真的出現(xiàn)OOM也不會直接崩潰導(dǎo)致直接退出程序
界面設(shè)計:
1.在視圖中加載你所需要的,而不是你所擁有茸时。因為用戶不可能同時看到所有東西贡定。最典型的例子就是ListView中的滑動加載。
2.如果數(shù)據(jù)特別大可都,此時應(yīng)該暗示用戶去點擊加載缓待,而不是直接加載。
3.合理運用分屏汹粤,轉(zhuǎn)屏等命斧,它是個雙刃劍田晚,因為它即可以使程序更加美觀功能更加完善嘱兼,但也相應(yīng)增加了資源開銷。
邏輯設(shè)計:
避免子類直接去控制父類中內(nèi)容贤徒,可以使用監(jiān)聽等方式去解決芹壕。
總結(jié):
其實優(yōu)化是需要單獨時間去做的汇四,在開發(fā)中如果特別仔細(xì)那可能的,但是一定要抓住重點優(yōu)化項踢涌,這樣就不會被自己代碼坑死了M酢!