記憶體泄露和記憶體溢出

來源:互聯網
上載者:User

標籤:android   des   style   blog   http   color   使用   os   

在以前我潛意思裡面一直覺得記憶體溢出和記憶體泄露是一個意思...今天有人拿這個來問我,我才想起來這兩個是有點區別的,真是有點不好意思哈哈。

不過他們還真有點關係,一般來說,記憶體泄露是記憶體溢出的一個原因之一,當然,記憶體溢出還有很多原因。

 

記憶體泄露:是指程式在運行過程中動態申請的記憶體空間不再使用後沒有及時釋放,從而很可能導致應用程式記憶體無限增長。更廣義的記憶體泄露包括未對系統的資源的及時釋放,比如不會再用的引用。

 

記憶體溢出:使用者在對其資料緩衝區操作時,超過了其緩衝區的邊界;尤其是對緩衝區寫操作時,緩衝區的溢出很可能導致程式的異常。

 

記憶體泄露的原因

1.資料庫的cursor沒有關閉。  
操作Sqlite資料庫時,Cursor是資料庫表中每一行的集合,Cursor提供了很多方法,可以很方便的讀取資料庫中的值, 
可以根據索引,列名等擷取資料庫中的值,通過遊標的方式可以調用moveToNext()移到下一行 
當我們操作完資料庫後,一定要記得調用Cursor對象的close()來關閉遊標,釋放資源。 
2.構造adapter沒有使用緩衝contentview。 
在繼承BaseAdapter時會讓我們重寫getView(int position, View   convertView, ViewGroup parent)方法, 
第二個參數convertView就是我們要用到的重用的對象 

@Override  public View getView(int position, View convertView, ViewGroup parent) {      ViewHolder vHolder = null;                 //如果convertView對象為空白則建立新對象,不為空白則複用      if (convertView == null) {          convertView = inflater.inflate(..., null);          // 建立 ViewHodler 對象          vHolder = new ViewHolder();          vHolder.img= (ImageView) convertView.findViewById(...);          vHolder.tv= (TextView) convertView                  .findViewById(...);          // 將ViewHodler儲存到Tag中          convertView.setTag(vHolder);      } else {                         //當convertView不為空白時,通過getTag()得到View          vHolder = (ViewHolder) convertView.getTag();      }      // 給對象賦值,修改顯示的值      vHolder.img.setImageBitmap(...);      vHolder.tv.setText(...);      return convertView;  }         //將顯示的View 封裝成類  static class ViewHolder {      TextView tv;      ImageView img;  }  

這裡只講使用方法,具體效能測試文章請見: 

ListView中getView的原理+如何在ListView中放置多個item 
http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html 
Android開發之ListView適配器(Adapter)最佳化 
http://shinfocom.iteye.com/blog/1231511 
3.調用registerReceiver()後未調用unregisterReceiver(). 
廣播接收者(BroadcastReceiver)經常在應用中用到,可以在多線程任務完成後發送廣播通知UI更新,也可以接收系統廣播實現一些功能 
可以通過代碼的方式註冊: 
IntentFilter postFilter = new IntentFilter(); 
postFilter.addAction(getPackageName() + ".background.job"); 
this.registerReceiver(receiver, postFilter); 
當我們Activity中使用了registerReceiver()方法註冊了BroadcastReceiver,一定要在Activity的生命週期內調用unregisterReceiver()方法取消註冊 
也就是說registerReceiver()和unregisterReceiver()方法一定要成對出現,通常我們可以重寫Activity的onDestory()方法: 

@Override  protected void onDestroy() {        this.unregisterReceiver(receiver);        super.onDestroy();  }  

4.未關閉InputStream/OutputStream。 
這個就不多說了,我們操作完輸入輸出資料流都要關閉流 


5.Bitmap使用後未調用recycle()。 
圖片處理不好是造成記憶體溢出的又一個頭號原因,(在我們的產品中也有體現), 
當我們處理完圖片之後可以通過調用recycle()方法來回收圖片對象 

if(!bitmap.isRecycled())  {      bitmap.recycle()  } 

除此之外: 
直接使用ImageView顯示bitmap會佔用較多資源,特別是圖片較大的時候,可能導致崩潰。 
使用BitmapFactory.Options設定inSampleSize, 這樣做可以減少對系統資源的要求。 
屬性值inSampleSize表示縮圖大小為原始圖片大小的幾分之一,即如果這個值為2,則取出的縮圖的寬和高都是原始圖片的1/2,圖片大小就為原始大小的1/4。 
BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();  
bitmapFactoryOptions.inJustDecodeBounds = true;  
bitmapFactoryOptions.inSampleSize = 2;  
// 這裡一定要將其設定回false,因為之前我們將其設定成了true  
// 設定inJustDecodeBounds為true後,decodeFile並不分配空間,即,BitmapFactory解碼出來的Bitmap為Null,但可計算出原始圖片的長度和寬度  
options.inJustDecodeBounds = false; 
Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);  
6.Context泄漏。 
    這是一個很隱晦的OutOfMemoryError的情況。先看一個Android官網提供的例子: 

private static Drawable sBackground;  @Override  protected void onCreate(Bundle state) {    super.onCreate(state);      TextView label = new TextView(this);    label.setText("Leaks are bad");      if (sBackground == null) {      sBackground = getDrawable(R.drawable.large_bitmap);    }    label.setBackgroundDrawable(sBackground);      setContentView(label);  }  

這段代碼效率很快,但同時又是極其錯誤的; 

在第一次螢幕方向切換時它泄露了一開始建立的Activity。當一個Drawable附加到一個 View上時, 
View會將其作為一個callback設定到Drawable上。上述的程式碼片段,意味著Drawable擁有一個TextView的引用, 
而TextView又擁有Activity(Context類型)的引用,換句話說,Drawable擁有了更多的對象引用。即使Activity被 銷毀,記憶體仍然不會被釋放。 
另外,對Context的引用超過它本身的生命週期,也會導致Context泄漏。所以盡量使用Application這種Context類型。 
這種Context擁有和應用程式一樣長的生命週期,並且不依賴Activity的生命週期。如果你打算儲存一個長時間的對象, 
並且其需要一個 Context,記得使用Application對象。你可以通過調用Context.getApplicationContext()或 Activity.getApplication()輕鬆得到Application對象。 
最近遇到一種情況引起了Context泄漏,就是在Activity銷毀時,裡面有其他線程沒有停。 
總結一下避免Context泄漏應該注意的問題: 
1.使用Application這種Context類型。 
2.注意對Context的引用不要超過它本身的生命週期。 
3.謹慎的使用“static”關鍵字。 
4.Context裡如果有線程,一定要在onDestroy()裡及時停掉。 
7.static關鍵字 
當類的成員變數聲明成static後,它是屬於類的而不是屬於對象的,如果我們將很大的資來源物件(Bitmap,context等)聲明成static,那麼這些資源不會隨著對象的回收而回收, 
會一直存在,所以在使用static關鍵字定義成員變數的時候要謹慎。

 

引用地址:http://zhanhao.iteye.com/blog/1463350

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.