Java内存泄漏

Java内存回收方式

Java判断对象是否可以回收使用的而是可达性分析算法。

在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。这个算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。


在Java语言里,可作为GC Roots对象的包括如下几种(摘自《深入理解Java虚拟机》):

*虚拟机栈(栈桢中的本地变量表)中的引用的对象
*方法区中的类静态属性引用的对象
*方法区中的常量引用的对象
*本地方法栈中JNI的引用的对象

内存泄漏的情形

因为静态成员保存在静态存储区,垃圾回收时不进行回收,只有在程序结束时才会被回收,
所以静态成员的生命期和程序的生命期一样长。在垃圾回收时,若本该被回收的部分持有静态成员的引用
则该部分就不会被回收,这种情况多次发生会导致内存不足从而导致内存泄漏。

****************************************************************************************************************
***以下内容有点乱,仅供参考***

1.静态成员引起的泄漏

public class TestActivity extends Activity {    
    private static Context sContext;    
    @Override
    protected void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sContext = this;

        RefWatcher refWatcher = App.getRefWatcher(this);

        refWatcher.watch(this);// 监控的对象
    }
}

分析:声明static后,sContext的生命周期将和Application一样长,Activity即使退出到桌面,Application依然存在->sContext依然存在,GC此时想回收Activity却发现Activity仍然被sContext(GC-ROOT连接着),导致回收不了,内存泄露。

2.单例模式引起的泄漏

public class DownloadManager {    
     private static DownloadManager instance;    
     private Task task ;    

     public static DownloadManager getInstance(){        
        if (instance == null) {
            instance = new DownloadManager();
        }        
        return instance;
    }    
    public Task newTask(){        
        this.task = new Task();        
        return task;
    }
}


public class Task {    
    private Call call;    
    public Call newCall(){        
        this.call = new Call();        
        return call;
    }
}


public class Call {    
    public void execute(){
        System.out.println("=========> execute call");
    }
}

Test

public class TestActivity extends Activity {    
    @Override
    protected void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        RefWatcher refWatcher = App.getRefWatcher(this);

        Task task = DownloadManager.getInstance().newTask();
        Call call = task.newCall();
        call.execute();
        refWatcher.watch(call);// 监控的对象
        call = null; // 无法回收,DownloadManager是静态单例,引用task,task引用了call,即使call置为空,也无法回收,切断GC_ROOT 联系即可避免内存泄露,即置task为空。
    }
}


补充Android相关:
如果某些单例需要使用到Context对象,推荐使用Application的context,不要使用Activity的context,否则容易导致内存泄露。单例对象的生命周期和Application一致,这样Application和单例对象就一起销毁。


3.内部类引起的泄漏

因为静态内部类不持有外部类的引用,而非静态内部类持有外部类的引用。若在外部类定义了一个非静态内部类的静态属性,即使令内部类为null,也无法回收内部类,导致内存泄漏。

public class OutterClass {    
    private static Inner inner;    
    class Inner{        
        public void list(){
            System.out.println("outter name is " + name);
        }
    }
}

所以优先使用静态内部类而不是非静态的,因为非静态内部类持有外部类引用可能导致垃圾回收失败。

补充Android相关:
如果你的静态内部类需要宿主Activity的引用来执行某些东西,你要将这个引用封装在一个WeakReference中,避免意外导致Activity泄露,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收 只被弱引用关联 的对象,只被 说明这个对象本身已经没有用处了。

*********************************************************************************************************************************
(来自编程之乐)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值