Java内存泄漏

来源:互联网 发布:ppt课件下载软件 编辑:程序博客网 时间:2024/06/11 05:58
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泄露,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收 只被弱引用关联 的对象,只被 说明这个对象本身已经没有用处了。

*********************************************************************************************************************************
(来自编程之乐)