內存泄露是指無用對象(再也不使用的對象)持續佔有內存或無用對象的內存得不到及時釋放,從而形成的內存空間的浪費稱爲內存泄露。內存泄露有時不嚴重且不易察覺,這樣開發者就不知道存在內存泄露,但有時也會很嚴重會提示你Out of memory
。java
長生命週期的對象持有短生命週期對象的引用就極可能發生內存泄露,儘管短生命週期對象已經再也不須要,可是由於長生命週期對象持有它的引用而致使不能被回收,這就是java中內存泄露的發生場景git
只有一個,那就是虛擬機佔用內存太高,致使OOM
(內存溢出),程序出錯。對於Android
應用來講,就是你的用戶打開一個Activity
,使用完以後關閉它,內存泄露;又打開,又關閉,又泄露;幾回以後,程序佔用內存超過系統限制FC
。github
Android
手機給應用分配的內存一般是8兆左右,若是處理內存處理不當很容易形成OutOfMemoryError
數據庫
OutOfMemoryError
主要由如下幾種狀況形成:緩存
數據庫Cursor
沒關。
當咱們操做完數據庫後,必定要調用close()
釋放資源。ide
構造Adapter
沒有使用緩存ContentView
。函數
@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; } static class ViewHolder { TextView tv; ImageView img; }
未取消註冊廣播接收者registerReceiver()
和unregisterReceiver()
要成對出現,一般須要在Activity
的onDestory()
方法去取消註冊廣播接收者。post
IO流未關閉 注意用完後及時關閉ui
Bitmap使用後未調用recycle()。this
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); }
這段代碼效率很快,但同時又是極其錯誤的:Drawable
擁有一個TextView
的引用,而TextView
又擁有Activity
(Context類型)的引用,Drawable
擁有了更多的對象引用。即便Activity
被銷燬,內存仍然不會被釋放。
另外,對Context的引用超過它自己的生命週期,也會致使Context泄漏。因此儘可能使用Application這種Context類型。因此若是打算保存一個長時間的對象時,要使用getApplicationContext()
。 最近遇到一種狀況引發了Context
泄漏,就是在Activity
銷燬時,裏面有其餘線程沒有停。
總結一下避免Contex
t泄漏應該注意的問題:
使用getApplicationContext()
類型。
注意對Context
的引用不要超過它自己的生命週期。
慎重的使用static
關鍵字。
Context
裏若是有線程,必定要在onDestroy()
裏及時停掉。
線程 線程也是形成內存泄露的一個重要的源頭。線程產生內存泄露的主要緣由在於線程生命週期的不可控。咱們來考慮下面一段代碼。
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); new MyThread().start(); } private class MyThread extends Thread{ @Override public void run() { super.run(); //耗時的操做 } } }
假設MyThread
的run
函數是一個很費時的操做,當調用finish
的時候Activity
會銷燬掉嗎?
事實上因爲咱們的線程是Activity
的內部類,因此MyThread
中保存了Activity
的一個引用,當MyThread
的run
函數沒有結束時,MyThread
是不會被銷燬的,所以它所引用的老的Activity
也不會被銷燬,所以就出現了內存泄露的問題。
Handler的使用,在Activity退出的時候注意移除(尤爲是循環的時候)
public class ThreadDemo extends Activity { private static final String TAG = "ThreadDemo"; private int count = 0; private Handler mHandler = new Handler(); private Runnable mRunnable = new Runnable() { public void run() { //爲了方便 查看,咱們用Log打印出來 Log.e(TAG, Thread.currentThread().getName() + " " +count); //每2秒執行一次 mHandler.postDelayed(mRunnable, 2000); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //經過Handler啓動線程 mHandler.post(mRunnable); } }//因此咱們在應用退出時,要將線程銷燬,咱們只要在Activity中的,onDestory()方法處理一下就OK了,以下代碼所示:@Override protected void onDestroy() { mHandler.removeCallbacks(mRunnable); super.onDestroy(); }