Android記憶體洩漏------常見場景
1、單例造成的記憶體洩露
單例模式是非常常用的設計模式,使用單例模式的類,只會產生一個物件,這個物件看起來像是一直佔用著記憶體,但這並不意味著就是浪費了記憶體,記憶體本來就是拿來裝東西的,只要這個物件一直都被高效的利用就不能叫做洩露。
實質是靜態變數引用Activity,在getInstance(Context context)方法中傳入的引數context如果是某個Activity,但是Activity的生命週期較短,而單例作為Static物件是全生命週期的,這樣當Activity結束後,單例物件仍然持有Activity的引用,GC機制無法判定為垃圾進行回收,此時發生記憶體洩露。
public class SingleInstanceTest { private static SingleInstanceTest sInstance; private Context mContext; private SingleInstanceTest(Context context){ //不能直接使用Activity或者短週期的Context this.mContext = context.getApplicationContext(); } public static SingleInstanceTest newInstance(Context context){ if(sInstance == null){ sInstance = new SingleInstanceTest(context); } return sInstance; } }
二、非靜態內部類導致記憶體洩露
非靜態內部類自動獲得外部類的強引用,而且它的生命週期甚至比外部類更長,這便埋下了記憶體洩露的隱患。如果一個 Activity 的非靜態內部類的生命週期比 Activity 更長,那麼 Activity 的記憶體便無法被回收,也就是發生了記憶體洩露,而且還有可能發生難以預防的空指標問題。解決辦法:將內部類宣告為靜態內部類,使其無法於外部類建立聯絡。
三、匿名內部類導致記憶體洩露
比方說Thread、Handler等
1、new 出一個匿名的 Thread,進行耗時的操作,如果 MainActivity 被銷燬而 Thread 中的耗時操作沒有結束的話,便會產生記憶體洩露;
解決方法:繼承 Thread 實現靜態內部類
2、new 出一個匿名的 Handler,這時如果 MainActivity 被銷燬,而 Handler 裡面的訊息還沒傳送完畢的話,Activity 的記憶體也不會被回收;
解決方法:
A:繼承 Handler 實現靜態內部類,以及在 Activity 的 onDestroy() 方法中,移除所有的訊息 mHandler.removeCallbacksAndMessages(null);
B:通過內部靜態類和弱引用的方法來及時釋放Activity,需要注意的是在Activity退出時記得清除掉訊息佇列中的訊息
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
。。。。。。
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
。。。。。。
}
}).start();
Message message = Message.obtain();
mHandler.sendMessageDelayed(message, 10000);
}
四、Handler導致記憶體洩露
其實還是匿名內部類的問題,都是錯誤的建立Handler,導致非靜態物件引用了Activity
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
五、資源未關閉導致記憶體洩露
對於使用了ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者登出,否則這些資源將不會被回收,造成記憶體洩漏。
還有其他:
手動註冊廣播時,退出時忘記 unregisterReceiver()
Service 執行完後忘記 stopSelf()
EventBus 等觀察者模式的框架忘記手動解除註冊
六、集合中物件沒清理造成的記憶體洩漏
Android主要的集合類
1、List 主要有ArrayList、LinkedList、Vector和Stack
2、Set 主要有HashSet 和 TreeSet
3、Map 主要有 HashMap 和 TreeMap
參考:https://blog.csdn.net/dbpggg/article/details/80825294
https://blog.csdn.net/gundumw100/article/details/69977700
https://www.jianshu.com/p/37bb043aef73
程式設計過程中,我們常常會把一些物件加入到集合中。在我們不再需要該物件時,如果沒有及時把它從集合中清理掉,就會導致這個集合佔用的記憶體越來越大。
同時如果這個集合是靜態的話,那情況就更嚴重了。
解決辦法:集合中不再使用的物件應及時釋放掉。應該在Activity的onDestroy()方法中,及時清理set裡的元素,避免無用物件繼續存在強引用。
public class MainActivity extends AppCompatActivity {
static List<Object> objectList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < 10; i++) {
Object obj = new Object();
objectList.add(obj);
obj = null;
}
}
public void onDestory(){
objectList.clear();
objectList = null;
}
}
七、ListView的記憶體洩漏問題
構造Adapter時,沒有使用快取的 convertView,導致記憶體洩露。
來向ListView提供每一個item所需要的view物件。初始時ListView會從BaseAdapter中根據當前的屏幕布局例項化一定數量的view物件,同時ListView會將這些view物件快取起來。如果我們不去使用convertView,而是每次都在getView()中重新例項化一個View物件的話,即浪費資源也浪費時間,也會使得記憶體佔用越來越大。
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
...
} else {
view = new Xxx(...);
...
}
return view;
}
八、static關鍵字所導致的記憶體洩漏
public class SecondActivity extends Activity{
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
SecondActivity.this.finish();
this.removeMessages(0);
}
};
private static Hehe hehe;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
haha = new Haha();
mHandler.sendEmptyMessageDelayed(0,5000);
}
class Hehe{
}
}
其實還是非靜態內部類的靜態引用。然後在5秒之後我們要finish掉這個activity,會造成什麼問題呢?我們知道,內部類和外部類之間是相互持有引用的,SecondActivity例項持有了hehe的引用,但這裡hehe是用static修飾的,上面說了,虛擬機器不會回收hehe這個物件,從而導致SecondActivity例項也得不到回收,造成記憶體溢位
解決辦法:只要在Activity的onDestroy方法裡把haha設為null就行啦