程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Android內存洩露實戰解析

Android內存洩露實戰解析

編輯:關於JAVA

Android內存洩露實戰解析。本站提示廣大學習愛好者:(Android內存洩露實戰解析)文章只能為提供參考,不一定能成為您想要的結果。以下是Android內存洩露實戰解析正文


Java是渣滓收受接管說話的一種,其長處是開辟者無需特地治理內存分派,下降了運用因為部分毛病(segmentation fault)招致瓦解,同時避免未釋放的內存把客棧(heap)擠爆的能夠,所以寫出來的代碼更加平安。

不幸的是,在Java中仍存在許多輕易招致內存洩露的邏輯能夠(logical leak)。假如不當心,你的Android運用很輕易糟蹋失落未釋放的內存,終究招致內存用光的毛病拋出(out-of-memory,OOM)。

1.普通內存洩露(traditional memory leak)的緣由是:當該對象的一切援用都曾經釋放了,對象仍未被釋放。(譯者注:Cursor忘卻封閉等)

2.邏輯內存洩露(logical memory leak)的緣由是:當運用不再須要這個對象,當仍未釋放該對象的一切援用。

假如持有對象的強援用,渣滓收受接管器是沒法在內存中收受接管這個對象。

在Android開辟中,最輕易激發的內存洩露成績的是Context。好比Activity的Context,就包括年夜量的內存援用,例如View Hierarchies和其他資本。一旦洩露了Context,也意味洩露它指向的一切對象。Android機械內存無限,太多的內存洩露輕易招致OOM。

檢測邏輯內存洩露須要客觀斷定,特殊是對象的性命周期其實不清楚。榮幸的是,Activity有著明白的性命周期,很輕易發明洩露的緣由。Activity.onDestroy()被視為Activity性命的停止,法式下去看,它應當被燒毀了,或許Android體系須要收受接管這些內存(譯者注:當內存不敷時,Android會收受接管看不見的Activity)。

假如這個辦法履行完,在客棧中仍存在持有該Activity的強援用,渣滓收受接管器就沒法把它標志成已收受接管的內存,而我們原來目標就是要收受接管它!

成果就是Activity存活在它的性命周期以外。

Activity是分量級對象,應當讓Android體系來處置它。但是,邏輯內存洩露老是在不經意間產生。(譯者注:已經試過一個Activity招致20M內存洩露)。在Android中,招致潛伏內存洩露的圈套不過乎兩種:

全局過程(process-global)的static變量。這個疏忽運用的狀況,持有Activity的強援用的怪物。

活在Activity性命周期以外的線程。沒有清空對Activity的強援用。

檢討一下你有無碰到以下的情形。

1.Static Activities

在類中界說了靜態Activity變量,把以後運轉的Activity實例賦值於這個靜態變量。

假如這個靜態變量在Activity性命周期停止後沒有清空,就招致內存洩露。由於static變量是貫串這個運用的性命周期的,所以被洩露的Activity就會一向存在於運用的過程中,不會被渣滓收受接管器收受接管。

static Activity activity;  
void setStaticActivity() {
  activity = this;
}
View saButton = findViewById(R.id.sa_button);
saButton.setOnClickListener(new View.OnClickListener() {   
   @Override public void onClick(View v) {
    setStaticActivity();
    nextActivity();
   }
});

2.Static Views

相似的情形會產生在單例形式中,假如Activity常常被用到,那末在內存中保留一個實例是很適用的。正如之前所述,強迫延伸Activity的性命周期是相當風險並且不用要的,不管若何都不克不及如許做。

特別情形:假如一個View初始化消耗年夜量資本,並且在一個Activity性命周期內堅持不變,那可以把它釀成static,加載到視圖樹上(View Hierachy),像如許,當Activity被燒毀時,應該釋放資本。(譯者注:示例代碼中並沒有釋放內存,把這個static view置null便可,然則照樣不建議用這個static view的辦法)

static view;  
void setStaticView() {
  view = findViewById(R.id.sv_button);
}
View svButton = findViewById(R.id.sv_button);
svButton.setOnClickListener(new View.OnClickListener() {   
  @Override public void onClick(View v) {
    setStaticView();
    nextActivity();
  }
});

3.Inner Classes

持續,假定Activity中有個外部類,如許做可以進步可讀性和封裝性。將如我們創立一個外部類,並且持有一個靜態變量的援用,祝賀,內存洩露就離你不遠了(譯者注:燒毀的時刻置空,嗯)。

private static Object inner;    
void createInnerClass() {    
  class InnerClass {
   }
  inner = new InnerClass();
}
View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {    
   @Override public void onClick(View v) {
     createInnerClass();
     nextActivity();
   }
});

外部類的優勢之一就是可以拜訪內部類,不幸的是,招致內存洩露的緣由,就是外部類持有內部類實例的強援用。

4.Anonymous Classes

類似地,匿名類也保護了內部類的援用。所之內存洩露很輕易產生,當你在Activity中界說了匿名的AsyncTsk。當異步義務在後台履行耗時義務時代,Activity不幸被燒毀了(譯者注:用戶加入,體系收受接管),這個被AsyncTask持有的Activity實例就不會被渣滓收受接管器收受接管,直到異步義務停止。

void startAsyncTask() {    
  new AsyncTask<Void, Void, Void>() {      
   @Override protected Void doInBackground(Void... params) {        
      while(true);
   }
  }.execute();
} 
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View aicButton = findViewById(R.id.at_button);
aicButton.setOnClickListener(new View.OnClickListener() {    
  @Override public void onClick(View v) {
     startAsyncTask();
     nextActivity();
  }
});

5.Handler

異樣事理,界說匿名的Runnable,用匿名類Handler履行。Runnable外部類會持有內部類的隱式援用,被傳遞到Handler的新聞隊列MessageQueue中,在Message新聞沒有被處置之前,Activity實例不會被燒毀了,因而招致內存洩露。

void createHandler() {    
  new Handler() {      
    @Override public void handleMessage(Message message) {        
      super.handleMessage(message);
    }
  }.postDelayed(new Runnable() {      
     @Override public void run() {        
       while(true);
    }
  }, Long.MAX_VALUE >> 1);
}
View hButton = findViewById(R.id.h_button);
hButton.setOnClickListener(new View.OnClickListener() {    
  @Override public void onClick(View v) {
    createHandler();
    nextActivity();
  }
});

6.Threads

我們再次經由過程Thread和TimerTask來展示內存洩露。

void spawnThread() {    
  new Thread() {      
   @Override public void run() {        
     while(true);
      }
  }.start();
}
View tButton = findViewById(R.id.t_button);
tButton.setOnClickListener(new View.OnClickListener() {   
  @Override public void onClick(View v) {
     spawnThread();
     nextActivity();
   }
});

7.TimerTask

只需是匿名類的實例,不論是否是在任務線程,都邑持有Activity的援用,招致內存洩露。

void scheduleTimer() {    
  new Timer().schedule(new TimerTask() {      
    @Override
   public void run() {        
       while(true);
   }
  }, Long.MAX_VALUE >> 1);
}
View ttButton = findViewById(R.id.tt_button);
ttButton.setOnClickListener(new View.OnClickListener() {    
  @Override public void onClick(View v) {
    scheduleTimer();
    nextActivity();
    }
});

8.Sensor Manager

最初,經由過程Context.getSystemService(int name)可以獲得體系辦事。這些辦事任務在各自的過程中,贊助運用處置後台義務,處置硬件交互。假如須要應用這些辦事,可以注冊監聽器,這會招致辦事持有了Context的援用,假如在Activity燒毀的時刻沒有刊出這些監聽器,會招致內存洩露。

void registerListener() {
    SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
    sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
View smButton = findViewById(R.id.sm_button);
smButton.setOnClickListener(new View.OnClickListener() {      
     @Override public void onClick(View v) {
      registerListener();
      nextActivity();
      }
});

總結

看過那末多會招致內存洩露的例子,輕易招致吃光手機的內存使渣滓收受接管處置更加頻發,乃至最壞的情形會招致OOM。渣滓收受接管的操作是很昂貴的開支,會招致肉眼可見的卡頓。所以,實例化的時刻留意持有的援用鏈,並常常停止內存洩露檢討。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved