內存01

Java編程中常常容易被忽視,但自己又十分重要的一個問題就是內存使用的問題。Android應用主要使用Java語言編寫,所以這個問題也一樣會在Android開發中出現。Android主要應用在嵌入式設備當中,而嵌入式設備因爲一些衆所周知的條件限制,一般都不會有很高的配置,特別是內存是比較有限的。若是咱們編寫的代碼當中有太多的對內存使用不當的地方,不免會使得咱們的設備運行緩慢,甚至是死機。爲了可以使得Android應用程序安全且快速的運行,Android的每一個應用程序都會使用一個專有的Dalvik虛擬機實例來運行,它是由Zygote服務進程孵化出來的,也就是說每一個應用程序都是在屬於本身的進程中運行的。一方面,若是程序在運行過程當中出現了內存泄漏的問題,僅僅會使得本身的進程被kill掉,而不會影響其餘進程(若是是system_process等系統進程出問題的話,則會引發系統重啓)。另外一方面Android爲不一樣類型的進程分配了不一樣的內存使用上限,若是應用進程使用的內存超過了這個上限,則會被系統視爲內存泄漏,從而被kill掉。html

然而內存的消耗甚至泄露是不可避免的,那麼首先只有養成良好的編程習慣,儘可能作到減小內存的使用,儘量的使垃圾內存獲得回收,這纔是解決問題的根本途徑,那麼咱們該怎樣編寫代碼呢?如下是本人的一些拙見以及網上達人們得一點點小小的建議:java

(一) 查詢數據庫沒有關閉遊標

程序中常常會進行查詢數據庫的操做,可是常常會有使用完畢Cursor後沒有關閉的狀況。若是咱們的查詢結果集比較小,對內存的消耗不容易被發現,只有在常時間大量操做的狀況下才會復現內存問題,這樣就會給之後的測試和問題排查帶來困難和風險。我以爲能夠這樣作:android

Cursor c = sqliteDb.query(TABLE_WORDS, word_full_projection,selection, null, null, null, null);
int numRows = c.getCount();
if(numRows > 0){
c.moveToFirst();
wordProfile account = new wordProfile();
account.createFromDb(c);
c.close();
return account;
}
c.close();sql

能夠採起將cursor裏的值賦值給實現Parcelable的類的成員變量,在所須要的地方相應取值即是,這樣編能夠作到及時關閉遊標,釋放資源。數據庫

(二) 構造Adapter時,沒有使用緩存的 convertView

以構造ListView的BaseAdapter爲例,在BaseAdapter中提升了方法:編程

public View getView(int position, View convertView, ViewGroup parent)緩存

來向ListView提供每個item所須要的view對象。初始時ListView會從BaseAdapter中根據當前的屏幕布局實例化必定數量的view對象,同時ListView會將這些view對象緩存起來。當向上滾動ListView時,原先位於最上面的list item的view對象會被回收,而後被用來構造新出現的最下面的list item。這個構造過程就是由getView()方法完成的,getView()的第二個形參 View convertView就是被緩存起來的list item的view對象(初始化時緩存中沒有view對象則convertView是null)。安全

由此能夠看出,若是咱們不去使用convertView,而是每次都在getView()中從新實例化一個View對象的話,即浪費資源也浪費時間,也會使得內存佔用愈來愈大。ListView回收list item的view對象的過程能夠查看:oop

android.widget.AbsListView.java –> void addScrapView(View scrap) 方法。佈局

(三) Bitmap對象不在使用時調用recycle()釋放內存

有時咱們會手工的操做Bitmap對象,若是一個Bitmap對象比較佔內存,當它不在被使用的時候,能夠調用Bitmap.recycle()方法回收此對象的像素所佔用的內存,但這不是必須的,視狀況而定。

(四) 將變量的做用域設置爲最小

最小化做用域意味着對垃圾收集器更快的可見。讓咱們舉一個例子。咱們有一個變量定義在方法級,當方法執行完畢後,也就是說,控制跳出方法後,則該變量將不會被引用。這也就意味着它已經能夠被垃圾回收。可是若是該變量是類級的,這時垃圾回收器就須要等待直到對該類的全部的引用都被
移除後才能進行垃圾回收。優化屬性的做用域是必須的。一樣這也是封裝原則。最小化做用域下降了經過包訪問變量並減小了耦合。

(五) 僅當你確實須要時才初始化

這個的意思是在你確實須要使用對象以前才分配內存。有些時候,須要在方法的開頭定義一些屬性。當定義這些屬性時,咱們極可能在定義它們的時候對其進行初始化。 這種狀況下, 若是在首次使用以前出現任何問題 (如出現異常),那麼已經初始化的變量所佔用的內存就被浪費了。

(六) 不要在循環中定義變量

這是大部份內存溢出的程序中的另外一個常見的錯誤。在循環中定義和初始化變量,對於循環的每次執行,該變量實例將在內存中建立並分配內存。在循環啓動後,第一個對象已經能夠回收了。這種額外的內存分配也會增長垃圾回收器的負擔。看一下下面的代碼片斷你將很容易的找出問題所在。有些時候,
確實有必要在循環內定義變量,不過必定要當心使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Vector; public class DeclareInLoopExample {
public void getDataIncorrect() throws Exception {
data = new Vector(11);
for (String str : data) {
String newStr = "";
// ...Some string operations
newStr = newStr + "X" + "X"; }
}
public void getDataCorrect() throws Exception {
data = new Vector(11);
String newStr = "";
for (String str : data)
{ // ...Some string operations
newStr = newStr + "X" + "X"; }
}
}

(七) 不要在循環中定義變量

這是和 String 相關的一些東西,全部對 String 進行的操做都將產生另外一個 String 對象。一般咱們使用的 String 連接操做,這個須要注意,它會致使一個很長的動態的查詢或產生一個 toString 方法中的 String。若是咱們使用+操做符來鏈接字符串,這些額外的對象將根據+產生。當有過多操做的時候會變成很大的開銷。使用不是不變對象,如 StringBuffer 來進行字符串連接操做。

(八) 不要在循環中定義變量

這會使得垃圾回收器的工做變得比較容易。 將重對象的引用設置爲 null。這將釋放對中對象的引用,使得垃圾回收器能當即釋放它們佔用的內存。

(九) 簡單的使用 finally 塊

finally 塊即便 try 或 catch 執行不成功的時候也會執行,也就是說即便在 try 或 catch 中出現了任何異常,finally 塊中的語句絕對也會執行。將清理內存的語句放在這裏以保證內存被清理。

(十) 分散對象建立或刪除的時間
集中在短期內大量建立新對象,特別是大對象,會致使忽然須要大量內存,JVM在面臨這種狀況時,只能進行主GC,以回收內存或整合內存碎片,從而增 加主GC的頻率。集中刪除對象,道理也是同樣的。它使得忽然出現了大量的垃圾對象,空閒空間必然減小,從而大大增長了下一次建立新對象時強制主GC的機 會。

其餘參考資料:http://www.chinaup.org/docs/toolbox/performance.html

相關文章
相關標籤/搜索