java的内存泄露是如何发生的,如何避免和发现

java的垃圾回收与内存泄露的关系:【新手可忽略不影响继续学习】
马克- to-win:马克 java社区:防盗版实名手机尾号: 73203。
马克-to-win:上一节讲了,(i)对象被置成null.(ii)局部对象(无需置成null)当程序运行到右大括号.(iii)匿名对象刚用完,垃圾回收线程就早早晚晚都能把它过去占的内存给回收了。这么说,java中难道就没有c++的内存泄露的问题了吗?(内存泄露的定义就是: 咱自己程序不用的内存,系统本应回收但由于各种原因却没有回收成功)马克-to-win:答案: 错,java中有内存泄露。下面我们就通过一个例子来说明。下面的例子中,Mark_to_win m作为实例是占有内存空间的。即使后来m = null;把它置为null,垃圾回收线程也回收不了它占有的空间。因为等我们后面集合框架学习了Vector以后,你就会知道:Vector v是一个类似数组的东西。马克-to-win:任何通过v.add(m);加到Vector里的东西,Vector都会保留一个对它的引用。正因为有这个引用,垃圾回收系统当中的有向图会认为,这个对象还是可达的,所以不会回收它的内存空间。因为size_Make_to_win非常大,(是maxMemory的0.8倍),所以系统最后就崩溃了。马克-to-win: 用专业术语讲,就是开始时是内存泄漏,泄露多了就造成内存溢出了,所以就曝出OutOfMemoryError的错误了。



例2.1.5
import java.util.Vector;
class Mark_to_win {
    long data;
}
public class Test {
    static Vector v = new Vector(10);
    public static void main(String[] args) throws InterruptedException {
/*maxMemory:获取系统所能提供的最大内存。*/      
        int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
        for (int i = 1; i < size_Make_to_win; i++) {
            Mark_to_win m = new Mark_to_win();
            v.add(m);
            m = null;
        }
        System.out.println("finish");
    }
}
输出结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Vector.ensureCapacityHelper(Unknown Source)
    at java.util.Vector.add(Unknown Source)
    at Test.main(Test.java:13)

上面例题之所以发生内存溢出的原因是:任何通过v.add(m);加到Vector里的东西,Vector都会保留一个对它的引用。马克-to-win: 所以只要我们通过v.remove(m)把这个引用去除。垃圾回收系统,就可以把不用的对象所占用的空间给回收了。这样就不报内存溢出的错误了。见下面的例子。  




例2.1.5_1

import java.util.Vector;
class Mark_to_win {
    long data;
}
public class Test {
    static Vector v = new Vector(10);
    public static void main(String[] args) throws InterruptedException {
/*maxMemory:获取系统所能提供的最大内存。*/      
        int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
        for (int i = 1; i < size_Make_to_win; i++) {
            Mark_to_win m = new Mark_to_win();
            v.add(m);
            // 这里是干你的事
            v.remove(m);
            m = null;
        }
        System.out.println("finish");
    }
}
输出结果:

finish


马克-to-win:通过以上的例子,说明了java中存在内存泄露。当泄露的很隐蔽时,后果就会很严重,导致内存溢出。通常市面上有几款查内存泄露的商业或非商业软件,帮助大家监控内存泄露的问题。Rational 公司的Purify,dmalloc,Optimizeit Profiler,JProbe Profiler,JinSight,mtrace,memwatch,马克-to-win:这里我们用jconsole来观察一下前面我们的程序内存泄漏的情况。例2.1.5是有内存泄露的,为了能够更好地观察这个程序内存泄漏的情况,我们加了一句,Thread.sleep(1),让它能够睡一毫秒。




例2.1.5_sleep版本:

import java.util.Vector;
class Mark_to_win {
    long data;
}
public class Test {
    static Vector v = new Vector(10);
    public static void main(String[] args) throws InterruptedException {
/*maxMemory:获取系统所能提供的最大内存。*/      
        int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
        for (int i = 1; i < size_Make_to_win; i++) {
            Mark_to_win m = new Mark_to_win();
            Thread.sleep(1);
            v.add(m);
            // 这里是干你的事
            m = null;
        }
        System.out.println("finish");
    }
}
输出结果:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Arrays.copyOf(Unknown Source)
    at java.util.Vector.ensureCapacityHelper(Unknown Source)
    at java.util.Vector.add(Unknown Source)
    at Test.main(Test.java:13)

以上程序运行起来以后,我们启动jconsole来监视它:(新手可忽略此章节)

马克-to-win:从上图可以看出,随着时间的流逝,内存消耗呈现稳定上升状态。基本可以判定,在未来一个时间点,系统会发生内存溢出。即系统崩溃。一旦 jconsole呈现这种图形,我们就需要修改程序了。这就是工具如何帮我们发现内存泄漏以及内存溢出。马克-to-win:例2.1.5_1是内存不泄露的, 我们加上Thread.sleep(1), 让它睡一下,从而用jconsole也观察一下, 看一下正常的内存使用应是什么情况的。




例2.1.5_1_sleep:
import java.util.Vector;
class Mark_to_win {
    long data;
}
public class Test {
    static Vector v = new Vector(10);
    public static void main(String[] args) throws InterruptedException {
/*maxMemory:获取系统所能提供的最大内存。*/      
        int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
        for (int i = 1; i < size_Make_to_win; i++) {
            Mark_to_win m = new Mark_to_win();
            Thread.sleep(1);
            v.add(m);
            // 这里是干你的事
            v.remove(m);
            m = null;
        }
        System.out.println("finish");
    }
}
输出结果:

finish

以上程序运行起来以后,我们启动jconsole来监视它:(新手可忽略此章节)

曲线呈现平稳波动。内存使用情况正常。