整理平時(shí)遇到Android內(nèi)存泄漏歸納分析心得
內(nèi)存泄漏:對于Java來說,就是new出來的Object 放在堆上無法被GC回收证九。
Java 中的內(nèi)存分配
- 靜態(tài)儲存區(qū):編譯時(shí)就分配好彭谁,在程序整個(gè)運(yùn)行期間都存在吸奴。它主要存放靜態(tài)數(shù)據(jù)和常量;
- 棧區(qū):當(dāng)方法執(zhí)行時(shí)缠局,會在棧區(qū)內(nèi)存中創(chuàng)建方法體內(nèi)部的局部變量则奥,方法結(jié)束后自動(dòng)釋放內(nèi)存;
- 堆區(qū):通常存放 new 出來的對象甩鳄。由 Java 垃圾回收器回收逞度。
四種引用類型的介紹
- 強(qiáng)引用(StrongReference):JVM 寧可拋出 OOM ,也不會讓 GC 回收具有強(qiáng)引用的對象妙啃;
- 軟引用(SoftReference):只有在內(nèi)存空間不足時(shí)档泽,才會被回的對象;
- 弱引用(WeakReference):在 GC 時(shí)揖赴,一旦發(fā)現(xiàn)了只具有弱引用的對象馆匿,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存燥滑;
- 虛引用(PhantomReference):任何時(shí)候都可以被GC回收渐北,當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對象時(shí),如果發(fā)現(xiàn)它還有虛引用铭拧,就會在回收對象的內(nèi)存之前赃蛛,把這個(gè)虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中恃锉。程序可以通過判斷引用隊(duì)列中是否存在該對象的虛引用,來了解這個(gè)對象是否將要被回收呕臂∑仆校可以用來作為GC回收Object的標(biāo)志。
內(nèi)存泄漏是指歧蒋?
我們常說的內(nèi)存泄漏是指new出來的Object無法被GC回收土砂,即為強(qiáng)引用。
怎么知道內(nèi)存泄漏呢谜洽?
內(nèi)存泄漏發(fā)生時(shí)的主要表現(xiàn)為內(nèi)存抖動(dòng)萝映,可用內(nèi)存慢慢變少。比如:在Android studio中的Android Monitor#Monitors可以觀察得到內(nèi)存抖動(dòng)情況阐虚。
Andriod中分析內(nèi)存泄漏的工具M(jìn)AT
- MAT(Memory Analyzer Tools)是一個(gè) Eclipse 插件序臂,它是一個(gè)快速、功能豐富的JAVA heap分析工具敌呈,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗贸宏。
- MAT 插件的下載地址: www.eclipse.org/mat
- MAT 使用方法介紹:http://www.cnblogs.com/larack/p/6071209.html
使用leakcanary檢測泄漏
- LeakCanary: 讓內(nèi)存泄露無所遁形http://www.liaohuqiu.net/cn/posts/leak-canary/
- LeakCanary:中文使用說明 http://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
LeakCanary的內(nèi)存泄露提示一般會包含三個(gè)部分:
- 第一部分(LeakSingle類的sInstance變量)
- 引用第二部分(LeakSingle類的mContext變量),
- 導(dǎo)致第三部分(MainActivity類的實(shí)例instance)泄露.
當(dāng)然檢測內(nèi)存泄漏的工具和方法有很多,就不一一列舉了磕洪,感興趣的可以網(wǎng)上查閱一下吭练。
常見的內(nèi)存泄漏案例
實(shí)例來講android中內(nèi)存泄漏分析解法和編寫代碼應(yīng)注意的事項(xiàng)。
1析显、單例模式引起的內(nèi)存泄露
由于單例模式的靜態(tài)特性鲫咽,使得它的生命周期和我們的應(yīng)用一樣長,如果讓單例無限制的持有Activity的強(qiáng)引用就會導(dǎo)致內(nèi)存泄漏谷异。
解決方案
- 將該屬性的引用方式改為弱引用;
- 如果傳入Context分尸,使用ApplicationContext;
泄漏代碼片段
public class UserInfoBean {
private static UserInfoBean userInfoBean;
private Context mContext;
private UserInfoBean(Context context) {
this.mContext = context;
}
public static UserInfoBean getUserInfoBean(Context context) {
if (userInfoBean == null) {
synchronized (UserInfoBean.class) {
if (userInfoBean == null) {
userInfoBean = new UserInfoBean(context);
}
}
}
return userInfoBean;
}
}
使用ApplicationContext
public class UserInfoBean {
private static UserInfoBean userInfoBean;
private Context mContext;
private UserInfoBean(Context context) {
this.mContext = context.getApplicationContext();
}
public static UserInfoBean getUserInfoBean(Context context) {
if (userInfoBean == null) {
synchronized (UserInfoBean.class) {
if (userInfoBean == null) {
userInfoBean = new UserInfoBean(context);
}
}
}
return userInfoBean;
}
}
或者代碼中用到的Context可以使用自己定義的MyApplication中的MyApplication.getInstance獲取。
泄漏代碼片段
private static CommonViewHelper mInstance;
private CommonViewHelper() {
}
public static CommonViewHelper getInstance() {
if (mInstance == null) {
synchronized (CommonViewHelper.class) {
if (mInstance == null) {
mInstance = new CommonViewHelper();
}
}
}
return mInstance;
}
private View mView = null;
public void setScrolledView(View scrolledView) {
mView = scrolledView;
}
使用WeakReference
private static CommonViewHelper mInstance;
private CommonViewHelper() {
}
public static CommonViewHelper getInstance() {
if (mInstance == null) {
synchronized (CommonViewHelper.class) {
if (mInstance == null) {
mInstance = new CommonViewHelper();
}
}
}
return mInstance;
}
//使用弱引用 防止內(nèi)存泄漏
private WeakReference<View> mViewWeakRef = null;
public void setScrolledView(View scrolledView) {
mViewWeakRef = new WeakReference<View>(scrolledView);
}
2歹嘹、Handler引起的內(nèi)存泄漏
Handler引起的內(nèi)存泄漏在開發(fā)中最為常見的箩绍。Handler、Message尺上、MessageQueue都是相互關(guān)聯(lián)在一起的材蛛,如果Handler發(fā)送的Message尚未被處理,那么該Message以及發(fā)送它的Handler對象都會被線程MessageQueue一直持有怎抛,保持到消息得到處理卑吭,而導(dǎo)致了Activity無法被垃圾回收器回收,而導(dǎo)致了內(nèi)存泄露马绝。由于Handler屬于TLS(Thread Local Storage)變量豆赏,生命周期和Activity是不一致的,因此這種實(shí)現(xiàn)方式很難保證跟Activity的生命周期一直,所以很容易無法釋放內(nèi)存掷邦。
解決方案
- 可以把Handler類放在單獨(dú)的類文件中白胀,或者使用靜態(tài)內(nèi)部類便可以避免泄露;
- 如果想在Handler內(nèi)部去調(diào)用所在的Activity,那么可以在handler內(nèi)部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式來達(dá)到斷開Handler與Activity之間存在引用關(guān)系的目的。
泄漏代碼片段
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendMessageDelayed(Message.obtain(), 60000*5);
}
在上面的代碼中發(fā)送了一個(gè)延時(shí)5分鐘執(zhí)行的Message耙饰,當(dāng)該Activity退出的時(shí)候纹笼,延時(shí)任務(wù)(Message)還在主線成的MessageQueue中等待,此時(shí)的Message持有Handler的強(qiáng)引用苟跪,并且由于Handler是我們的Activity類的非靜態(tài)內(nèi)部類,所以Handler會持有該Activity的強(qiáng)引用蔓涧,此時(shí)該Activity退出時(shí)無法進(jìn)行內(nèi)存回收件已,造成內(nèi)存泄漏。
正確代碼片段
解決辦法:將Handler聲明為靜態(tài)內(nèi)部類元暴,這樣它就不會持有外部類的引用了篷扩,Handler的的生命周期就與Activity無關(guān)了。不過倘若用到Context等外部類的非static對象茉盏,還是應(yīng)該通過使用Application中與應(yīng)用同生命周期的Context比較合適鉴未。
private final MyHandler mHandler = new MyHandler(this);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendMessageDelayed(Message.obtain(), 60000*5);
}
private static final class MyHandler extends Handler {
private WeakReference<HomeMainActivity> mActivity;
public MyHandler(HomeMainActivity mainActivity) {
mActivity = new WeakReference<>(mainActivity);
//or
//mActivity=mainActivity.getApplicationContext;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HomeMainActivity mainActivity = mActivity.get();
if (null != mActivity) {
//相關(guān)處理
}
}
}
雖然我們結(jié)束了Activity的內(nèi)存泄漏問題,但是經(jīng)過Handler發(fā)送的延時(shí)消息還在MessageQueue中鸠姨,Looper也在等待處理消息铜秆,所以我們要在Activity銷毀的時(shí)候處理掉隊(duì)列中的消息。
@Override
protected void onDestroy() {
super.onDestroy();
//傳入null讶迁,就表示移除所有Message和Runnable
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
mHandler = null;
}
3连茧、InnerClass匿名內(nèi)部類引起的內(nèi)存泄漏(非靜態(tài)內(nèi)部類、匿名內(nèi)部類巍糯、線程 )
在Java中啸驯,非靜態(tài)內(nèi)部類 和 匿名類 都會潛在的引用它們所屬的外部類,但是祟峦,靜態(tài)內(nèi)部類卻不會罚斗。如果這個(gè)非靜態(tài)內(nèi)部類實(shí)例做了一些耗時(shí)的操作,就會造成外圍對象不會被回收宅楞,從而導(dǎo)致內(nèi)存泄漏针姿。
匿名內(nèi)部類的類型可以是如下幾種方式。
- 接口匿名內(nèi)部類
- 抽象類匿名內(nèi)部類
- 類匿名內(nèi)部類
解決方案
- 將內(nèi)部類變成靜態(tài)內(nèi)部類;
- 如果有強(qiáng)引用Activity中的屬性咱筛,則將該屬性的引用方式改為弱引用;
- 在業(yè)務(wù)允許的情況下搓幌,當(dāng)Activity執(zhí)行onDestory時(shí),結(jié)束這些耗時(shí)任務(wù);
泄漏代碼片段
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
//這兒發(fā)生泄漏
public void test() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
正確代碼片段
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
//加上static迅箩,變成靜態(tài)匿名內(nèi)部類
public static void test() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
Android開發(fā)經(jīng)常會繼承實(shí)現(xiàn) Activity 或者 Fragment 或者 View溉愁。如果使用了匿名類,而又被異步線程所引用,如果沒有任何措施同樣會導(dǎo)致內(nèi)存泄漏的:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inner_bad);
Runnable runnable1 = new MyRunnable();
Runnable runnable2 = new Runnable() {
@Override
public void run() {
}
};
}
private static class MyRunnable implements Runnable{
@Override
public void run() {
}
}
}
runnable1 和 runnable2的區(qū)別就是拐揭,runnable2使用了匿名內(nèi)部類撤蟆,runnable1是沒有什么特別的。是個(gè)靜態(tài)內(nèi)部類堂污。但runnable2多出了一個(gè)MainActivity的引用家肯,若是這個(gè)引用再傳入到一個(gè)異步線程,此線程在和Activity生命周期不一致的時(shí)候盟猖,也就造成了Activity的泄露讨衣。
泄漏代碼片段
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer();
}
//這兒發(fā)生泄漏
void timer(){
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while(true);
}
},1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)
}
}
正確代碼片段
private TimerTask timerTask ;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer();
}
void timer(){
timerTask = new MyTimerTask() ;
new Timer().schedule( timerTask ,1000 ); // 1秒后啟動(dòng)一個(gè)任務(wù)
}
private static class MyTimerTask extends TimerTask{
@Override
public void run() {
while(true){
Log.d( "ttttttttt" , "timerTask" ) ;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消定時(shí)任務(wù)
if ( timerTask != null ){
timerTask.cancel() ;
}
}
}
注意:在網(wǎng)上看到一些資料說,解決TimerTask內(nèi)存泄漏可以使用在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel式镐。經(jīng)過測試反镇,證明單單使用在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel,還是有內(nèi)存泄漏的問題娘汞。所以一定要用靜態(tài)內(nèi)部類配合使用歹茶。
4、集合引發(fā)的內(nèi)存泄漏
我們通常把一些對象的引用加入到了集合容器(比如ArrayList)中你弦,當(dāng)我們不需要該對象時(shí)惊豺,并沒有把它的引用從集合中清理掉,這樣這個(gè)集合就會越來越大禽作。如果這個(gè)集合是static的話尸昧,那情況就更嚴(yán)重了。所以要在退出程序之前领迈,將集合里的東西clear彻磁,然后置為null,再退出程序狸捅。
解決方案
在Activity退出之前衷蜓,將集合里的東西clear,然后置為null尘喝,再退出程序磁浇。
正確代碼片段
private List<String> nameList;
private List<Fragment> list;
@Override
public void onDestroy() {
super.onDestroy();
if (nameList != null){
nameList.clear();
nameList = null;
}
if (list != null){
list.clear();
list = null;
}
}
5、 Activity Context 的不正確使用引起的內(nèi)存泄漏
在Android應(yīng)用程序中通承嗤剩可以使用兩種Context對象:Activity和Application置吓。當(dāng)類或方法需要Context對象的時(shí)候常見的做法是使用第一個(gè)作為Context參數(shù)。這樣就意味著View對象對整個(gè)Activity保持引用缔赠,因此也就保持對Activty的所有的引用衍锚。
假設(shè)一個(gè)場景,當(dāng)應(yīng)用程序有個(gè)比較大的Bitmap類型的圖片嗤堰,每次旋轉(zhuǎn)是都重新加載圖片所用的時(shí)間較多戴质。為了提高屏幕旋轉(zhuǎn)是Activity的創(chuàng)建速度,最簡單的方法時(shí)將這個(gè)Bitmap對象使用Static修飾。 當(dāng)一個(gè)Drawable綁定在View上告匠,實(shí)際上這個(gè)View對象就會成為這份Drawable的一個(gè)Callback成員變量戈抄。而靜態(tài)變量的生命周期要長于Activity。導(dǎo)致了當(dāng)旋轉(zhuǎn)屏幕時(shí)后专,Activity無法被回收划鸽,而造成內(nèi)存泄露。
解決方案
- 使用ApplicationContext代替Activity#Context戚哎,因?yàn)锳pplicationContext會隨著應(yīng)用程序的存在而存在裸诽,而不依賴于activity的生命周期;
- 對Context的引用不要超過它本身的生命周期建瘫,慎重的對Context使用“static”關(guān)鍵字崭捍。Context里如果有線程,一定要在onDestroy()里及時(shí)停掉啰脚。
泄漏代碼片段
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView tv = new TextView(this);
tv.setText("引起的內(nèi)存泄漏");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
tv.setBackgroundDrawable(sBackground);
setContentView(tv);
}
正確代碼片段
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView tv = new TextView(this);
tv.setText("引起的內(nèi)存泄漏");
if (sBackground == null) {
sBackground = getApplicationContext().getDrawable(R.drawable.large_bitmap);
}
tv.setBackgroundDrawable(sBackground);
setContentView(tv);
}
6、構(gòu)造Adapter時(shí)实夹,沒有使用緩存的ConvertView引起的內(nèi)存泄漏
- 初始時(shí)ListView會從Adapter中根據(jù)當(dāng)前的屏幕布局實(shí)例化一定數(shù)量的View對象橄浓,同時(shí)ListView會將這些View對象 緩存起來。
- 當(dāng)向上滾動(dòng)ListView時(shí)亮航,原先位于最上面的List Item的View對象會被回收荸实,然后被用來構(gòu)造新出現(xiàn)的最下面的List Item。
- 這個(gè)構(gòu)造過程就是由getView()方法完成的缴淋,getView()的第二個(gè)形參View ConvertView就是被緩存起來的List Item的View對象(初始化時(shí)緩存中沒有View對象則ConvertView是null)准给。
7、BroadcastReceiver重抖、ContentObserver露氮、File、Cursor钟沛、Stream畔规、Bitmap等資源引起的內(nèi)存泄漏
資源性對象比如(Cursor,F(xiàn)ile文件等)往往都用了一些緩沖恨统,我們在不使用的時(shí)候叁扫,應(yīng)該及時(shí)關(guān)閉它們,以便它們的緩沖及時(shí)回收內(nèi)存畜埋。它們的緩沖不僅存在于 java虛擬機(jī)內(nèi)莫绣,還存在于java虛擬機(jī)外。如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們悠鞍,往往會造成內(nèi)存泄漏对室。因?yàn)橛行┵Y源性對象,比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒有關(guān)閉它,它自己會調(diào)close()關(guān)閉)软驰,如果我們沒有關(guān)閉它涧窒,系統(tǒng)在回收它時(shí)也會關(guān)閉它,但是這樣的效率太低了锭亏。因此對于資源性對象在不使用的時(shí)候纠吴,應(yīng)該調(diào)用它的close()函數(shù),將其關(guān)閉掉慧瘤,然后才置為null. 在我們的程序退出時(shí)一定要確保我們的資源性對象已經(jīng)關(guān)閉戴已。
調(diào)用onRecycled()
@Override
public void onRecycled() {
reset();
mSinglePicArea.onRecycled();
}
在View中調(diào)用reset()
public void reset() {
if (mHasRecyled) {
return;
}
...
SubAreaShell.recycle(mActionBtnShell);
mActionBtnShell = null;
...
mIsDoingAvatartRedPocketAnim = false;
if (mAvatarArea != null) {
mAvatarArea.reset();
}
if (mNickNameArea != null) {
mNickNameArea.reset();
}
}
在自定義 View中取屬性值,調(diào)用recycle()
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArcMenu,defStyleAttr,0);
a.recycle();
8锅减、注冊監(jiān)聽器的泄漏引起的內(nèi)存泄漏
系統(tǒng)服務(wù)可以通過Context.getSystemService 獲取糖儡,它們負(fù)責(zé)執(zhí)行某些后臺任務(wù),或者為硬件訪問提供接口怔匣。如果Context 對象想要在服務(wù)內(nèi)部的事件發(fā)生時(shí)被通知握联,那就需要把自己注冊到服務(wù)的監(jiān)聽器中。然而每瞒,這會讓服務(wù)持有Activity 的引用金闽,如果在Activity onDestory時(shí)沒有釋放掉引用就會內(nèi)存泄漏。
解決方案
- 使用ApplicationContext代替ActivityContext;
- 在Activity執(zhí)行onDestory時(shí)剿骨,調(diào)用反注冊;
泄漏代碼片段
mSensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
正確代碼片段
mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
下面是容易造成內(nèi)存泄漏的系統(tǒng)服務(wù)
泄漏代碼片段
InputMethodManager imm = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
正確代碼片段
protected void onDetachedFromWindow() {
if (this.mActionShell != null) {
this.mActionShell.setOnClickListener((OnAreaClickListener)null);
}
if (this.mButtonShell != null) {
this.mButtonShell.setOnClickListener((OnAreaClickListener)null);
}
if (this.mCountShell != this.mCountShell) {
this.mCountShell.setOnClickListener((OnAreaClickListener)null);
}
super.onDetachedFromWindow();
}
9代芜、WebView引起的內(nèi)存泄漏
當(dāng)我們不要使用WebView對象時(shí),應(yīng)該調(diào)用它的destory()函數(shù)來銷毀它浓利,并釋放其占用的內(nèi)存挤庇,否則其占用的內(nèi)存長期也不能被回收,從而造成內(nèi)存泄露贷掖。
解決方案
為webView開啟另外一個(gè)進(jìn)程嫡秕,通過AIDL與主線程進(jìn)行通信,WebView所在的進(jìn)程可以根據(jù)業(yè)務(wù)的需要選擇合適的時(shí)機(jī)進(jìn)行銷毀羽资,從而達(dá)到內(nèi)存的完整釋放淘菩。
使用中遇到問題可以參考該鏈接:Android之Android WebView常見問題及解決方案匯總 http://www.cnblogs.com/lee0oo0/p/4026774.html
總結(jié)
其他常見的引起內(nèi)存泄漏原因:
- Bitmap在不使用的時(shí)候沒有使用recycle()釋放內(nèi)存。
- 非靜態(tài)內(nèi)部類的靜態(tài)實(shí)例容易造成內(nèi)存泄漏:即一個(gè)類中如果你不能夠控制它其中內(nèi)部類的生命周期(譬如Activity中的一些特殊Handler等)屠升,則盡量使用靜態(tài)類和弱引用來處理(譬如ViewRoot的實(shí)現(xiàn))潮改。
- 警惕線程未終止造成的內(nèi)存泄露;譬如在Activity中關(guān)聯(lián)了一個(gè)生命周期超過Activity的Thread腹暖,在退出Activity時(shí)切記結(jié)束線程汇在。一個(gè)典型的例子就是HandlerThread的run方法是一個(gè)死循環(huán),它不會自己結(jié)束脏答,線程的生命周期超過了Activity生命周期糕殉,我們必須手動(dòng)在Activity的銷毀方法中調(diào)用thread.getLooper().quit();才不會泄露亩鬼。
- 對象的注冊與反注冊沒有成對出現(xiàn)造成的內(nèi)存泄露;譬如注冊廣播接收器阿蝶、注冊觀察者(典型的譬如數(shù)據(jù)庫的監(jiān)聽)等雳锋。
- 創(chuàng)建與關(guān)閉沒有成對出現(xiàn)造成的泄露;譬如Cursor資源必須手動(dòng)關(guān)閉羡洁,WebView必須手動(dòng)銷毀玷过,流等對象必須手動(dòng)關(guān)閉等。
- 不要在執(zhí)行頻率很高的方法或者循環(huán)中創(chuàng)建對象(比如onMeasure)筑煮,可以使用HashTable等創(chuàng)建一組對象容器從容器中取那些對象辛蚊,而不用每次new與釋放。
- 對Context持有一個(gè)過長的引用真仲。對Context的引用超過它本身的生命周期袋马。Android應(yīng)用程序限制使用的堆內(nèi)存是16M 。
- 注意對Context的引用不要超過它本身的生命周期 秸应。
- Context里假設(shè)有線程虑凛,一定要在onDestory()里及時(shí)停掉。
- 當(dāng)類成員變量聲明為static后软啼,它屬于類而不是屬于對象卧檐。假設(shè)我們將非常大的資源對象(Bitmap。context等待)聲明static焰宣。那么這些資源不會被回收的回收目標(biāo)。它會一直存在捕仔。因此匕积,使用statickeyword成員變量定義時(shí)要小心。