概念
內存溢出(Out of memory):系統會給每個APP分配內存,默認16M內存,每個手機廠商的默認值不一樣,當APP所需要的內存大於了系統分配的內存,就會造成內存溢出;內存溢出就是分配的內存被用光了,不夠用了。
內存泄漏(Memory leak):當一個對象不再使用了,本應該被垃圾回收器(GC)回收,但是這個對象由於被其他正在使用的對象所持有,造成無法被回收,導致一部分內存一直被佔着。
內存泄漏和內存溢出的關係:內存泄露過多會導致內存溢出。內存泄露導致一部分內存沒有被回收,一直被佔着,可利用內存變少了,當泄露過多 時,可利用的內存越來越少,就會引起內存溢出了。
原因及優化
內存溢出
- Bitmap使用不當
1、使用Bitmap對象要用recycle釋放
2、使用軟引用、弱引用
3、使用圖片緩存技術(例如:LruCache)
內存泄漏
在內存比較低的系統上對大量的圖片、音頻、視頻處理
優化: 建議使用第三方,或者JNI來進行處理Handler使用不當
例如:
在日常開發中我們通常都是這樣定義Handler對象:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
dosomething();
}
};
但是這樣也存在着一個隱藏的問題:在Activity中使用Handler創建匿名內部類會隱式的持有外部Activity對象的引用,當子線程使用Handler暫時無法完成異步任務時,handler對象無法銷燬,同時由於隱式的持有activity對象的引用,造成activity對象以及相關的組件與資源文件同樣無法銷燬,造成內存泄露。我們普通的Handler,平時使用時也是可以不用考慮這些的,但是當你在子線程中有耗時操作通知UI時,要考慮到這一點,以免activity結束時子線程持有Handler對象,導致Activity無法被釋放。
優化解決方法:
1)使用靜態變量定義Handler
static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
dosomething();
}
};
這樣的話,handler對象由於是靜態類型無法持有外部activity的引用,但是這樣Handler不再持有外部類的引用,導致程序不允許在Handler中操作activity中的對象了,這時候我們需要在Handler對象中增加一個隊activity的弱引用;
2)Handler使用弱引用
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
最後在ondestory方法中將後面的消息移除
在activity執行onDestory時,判斷是否有handler已經執行完成,否則做相關邏輯操作;
mhanHandler.removeCallbacksAndMessages(null);
- Context對象使用不當
例如自定義單例對象
public class Single {
private static Single instance;
private Context context;
private Object obj = new Object();
private Single(Context context) {
this.context = context;
}
public static Single getInstance(Context context) {
if (instance == null) {
synchronized(obj) {
if (instance == null) {
instance = new Single(context);
}
}
}
return instance;
}
}
優化解決方法:使用全局的的Context或者context.getApplication();
單列模式應該儘量少持有生命週期不同的外部對象,一旦持有該對象的時候,必須在該對象的生命週期結束前制null
- 非靜態內部類,靜態實例化
static關鍵字修飾的變量由於生命週期過長,容易造成內存泄漏
例如:
public class MyActivity extends AppCompatActivity {
/** 靜態成員變量*/
public static InnerClass innerClass = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
innerClass = new InnerClass();
}
class InnerClass {
public void test() {
//TODO ...
}
}
}
這裏內部類InnerClass隱式的持有外部類MyActivity的引用,而在MyActivity的onCreate方法中調用了 innerClass = new InnerClass();
這樣innerClass就會在MyActivity創建的時候是有了他的引用,而innerClass是靜態類型的不會被垃圾回收,MyActivity在執行onDestory方法的時候由於被innerClass持有了引用而無法被回收,所以這樣MyActivity就總是被innerClass持有而無法回收造成內存泄露。
優化解決方法:儘量少使用靜態變量,一定要使用要及時進行制null處理
屬性動畫或循環動畫使用不當
優化解決方法:在Activity中使用了屬性循環動畫,在onDestroy()方法中未正確停止動畫BraodcastReceiver、File、Cursor等資源的使用未及時關閉
優化解決方法:在銷燬activity時,應該及時銷燬或者回收框架使用了註冊方法而未反註冊
例如:
使用的事件總線框架-EventBus,當我們需要註冊某個Activity時需要在onCreate中:
EventBus.getDefault().register(this);
然後這樣之後就沒有其他操作的話就會出現內存泄露的情況,因爲EventBus對象會是有該Activity的引用,即使執行了改Activity的onDestory方法,由於被EventBus隱式的持有了該對象的引用,造成其無法被回收。
優化解決方法:需要在onDestory方法中執行反註冊:
EventBus.getDefault().unregister(this);
參考文章
https://blog.csdn.net/qq_28607155/article/details/76148989
https://blog.csdn.net/u014674558/article/details/62234008
https://blog.csdn.net/guolin_blog/article/details/9316683