內存泄漏原理
真的很詳細, 獲益匪淺java
博客原文
Java內存泄漏的根本緣由是什麼呢?長生命週期的對象持有短生命週期對象的引用就極可能發生內存泄漏,儘管短生命週期對象已經再也不須要,可是由於長生命週期持有它的引用而致使不能被回收,這就是Java中內存泄漏的發生場景。segmentfault
知識點:
1, new的對象及成員變量在堆中, 這部份內存在不使用時由GC來回收
2, 方法體的局部變量在棧中,方法執行完,則局部變量持有的內存會被自動釋放,也就是再也不持有對象的引用
3, 當一個對象的任務已經執行完畢,正常變量名的引用已經結束, 可是其餘還在執行的對象, 還間接持有該對象, 致使內存泄漏
4, 判斷對象是否可達 bash
何爲不可達
(1)改變對象的引用,如置爲null或者指向其餘對象。
Object x=new Object();//object1
Object y=new Object();//object2
x=y;//object1 變爲垃圾
x=y=null;//object2 變爲垃圾
(2)超出做用域
if(i==0){
Object x=new Object();//object1
}
//括號結束後object1將沒法被引用,變爲垃圾
(3)類嵌套致使未徹底釋放 class A{ A a; } A x= new A();//分配一個空間 x.a= new A();//又分配了一個空間 x=null;//將會產生兩個垃圾
(4)線程中的垃圾 class A implements Runnable{
void run(){ //.... }
}
//main
A x=new A();//object1
x.start();
x=null;
//等線程執行完後object1才被認定爲垃圾
這樣看,確實在代碼執行過程當中會產生不少垃圾,不過不用擔憂,java能夠有效地處理他們。ide
5, 內存泄漏示意圖
post
class Activity{
r = new Runnable(){
run(){//耗時操做}
}
t = new Thread(){}
}
複製代碼
當退出activity, 但耗時操做還未結束時, 就會內存泄漏, 由於new接口在java語法裏, 就是建立匿名內部類的意思, java語法裏接口是不能new的, 而非靜態內部類會持有外部類, 因此Activity類雖然已經結束, 理論應該被GC回收, 可是還在被Runnable持有, 致使內存泄漏this
解決方法:spa
class Activity{
MyRunnable r = new MyRunnable()
void create()
private static class MyThread extends Thread {
}
private static class MyRunnable implements Runnable{
}
}
複製代碼
改爲靜態內部類, 由於靜態內部類, 並不在靜態區, 建立出來後就是一個普通的對象, 生命週期跟隨普通類.net
calss Util{
private static Context context;
public static void utils(Context context){
this.context = context;
}
}
複製代碼
由於context被靜態持有後, 生命週期太長, 當context的Activity已經退出時, 應該被回收, 但依然被Util持有, 致使Activity對象的內存沒法回收,線程
解決辦法:
1,用Application的context, 相似於Dialog這種必需要Activity的Context的, 可使用其餘方法,
2,不要將context傳給靜態變量,也不要傳給其餘生命週期可能會長於Activity的對象3d
其實用了RxJava以後, 如今Handler用的比較少了, 咱們用Handler就是由於線程切換, 既然有其餘線程, 那其餘線程的生命週期就可能比Activity長, 當Activity結束後, Handler還在執行, 就會內存泄漏
calss Activity{
Handler mHandler = new Handler(){
handlerMessage(Message msg){//響應}
}
onCreate(){
mHandler.sendEmptyMessageDelayed(0, 1000 * 2);//延時
}
}
複製代碼
解決辦法:
1, 引入軟引用
強引用的對象, 就毫不收回,
軟引用的對象,是能不收回就不收回,這裏的能不收回就是指內存足夠的狀況,
弱引用的對象,是發現就收回,可是通常狀況下不會發現
private static class NoLeakHandler extends Handler{
private WeakReference<NoLeakActivity> mActivity;
public NoLeakHandler(NoLeakActivity activity){
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
複製代碼
每次使用前注意mActivity的判空
2, 及時解除關係,這樣也能防止泄露, 由於handler已經不在持有Activity
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
複製代碼
Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}
複製代碼
Vector的生命週期跟隨整個應用, 雖然變量名o再也不持有對象, 但v一直持有, 致使new出來的對象沒法回收, 致使內存泄漏
解決辦法 將v置爲null
Bitmap 沒調用 recycle()方法,對於 Bitmap 對象在不使用時,咱們應該先調用 recycle() 釋放內存,而後才它設置爲 null. 由於加載 Bitmap 對象的內存空間,一部分是 java 的,一部分 C 的(由於 Bitmap 分配的底層是經過 JNI 調用的 )。 而這個 recyle() 就是針對 C 部分的內存釋放
對 Activity 等組件的引用應該控制在 Activity 的生命週期以內; 若是不能就考慮使用 getApplicationContext 或者 getApplication,以免 Activity 被外部長生命週期的對象引用而泄露
對於生命週期比Activity長的內部類對象,而且內部類中使用了外部類的成員變量,能夠這樣作避免內存泄漏
將內部類改成靜態內部類
靜態內部類中使用弱引用來引用外部類的成員變量
另一篇講原理
這個做者我很喜歡
一個不錯的評論 1.JVM 的內存模型和 Android 虛擬機是有一點區別,JVM 基於棧,但 Android 虛擬機基於寄存器,因此 Android 虛擬機棧中不存在局部變量表這種說法,裏頭實際上是一組虛擬寄存器。 2.博主拿對象的引用在棧上的狀況來解釋內存泄露是不恰當的,由於棧上引用的對象是不會致使內存泄露的,當一個方法執行完的時候,對象引用天然就置空了。 只有在成員變量上引用的對象會致使內存泄露,由於引用是分配在堆上的。