当你从c&c++转到一门具有垃圾回收功能的语言时,程序员的工作就会变得更加容易,因为你用完对象,他们会被自动回收,但是,java程序员真的不需要考虑内存泄露吗? 其实不然

1.举个例子-看你能否找出内存泄漏

import java.util.Arrays;public class Stack {    private Object[] elements;    private int size = 0;    private static final int DEFAULT_INITIAL_CAPACITY = 16;    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }    public void push(Object e) {        ensureCapacity();
        elements[size++] = e;
    }    public Object pop() {        if (size == 0)            throw new EmptyStackException();        return elements[--size];
    }    private void ensureCapacity() {        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

1.1原因分析

上述程序并没有明显的错误,但是这段程序有一个内存泄漏,随着GC活动的增加,或者内存占用的不断增加,程序性能的降低就会表现出来,严重时可导致内存泄漏,但是这种失败情况相对较少。
代码的主要问题在pop函数,下面通过这张图示展现
假设这个栈一直增长,增长后如下图所示
iOS培训,Swift培训,苹果开发培训,移动开发培训
当进行大量的pop操作时,由于引用未进行置空,gc是不会释放的,如下图所示
iOS培训,Swift培训,苹果开发培训,移动开发培训

从上图中看以看出,如果栈先增长,在收缩,那么从栈中弹出的对象将不会被当作垃圾回收,即使程序不再使用栈中的这些队象,他们也不会回收,因为栈中仍然保存这对象的引用,俗称过期引用,这个内存泄露很隐蔽。

1.2解决方法

public Object pop() {    if (size == 0)    throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;    return result;
}

一旦引用过期,清空这些引用,将引用置空。
iOS培训,Swift培训,苹果开发培训,移动开发培训

2.缓存泄漏

内存泄漏的另一个常见来源是缓存,一旦你把对象引用放入到缓存中,他就很容易遗忘,对于这个问题,可以使用WeakHashMap代表缓存,此种Map的特点是,当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值

2.1代码示例

/** * Created by liuroy on 2017/2/25. */import java.util.HashMap;import java.util.Map;import java.util.WeakHashMap;import java.util.concurrent.TimeUnit;public class Test {    static Map wMap = new WeakHashMap();    static Map map = new HashMap();    public static void init(){
        String ref1= new String("obejct1");
        String ref2 = new String("obejct2");
        String ref3 = new String ("obejct3");
        String ref4 = new String ("obejct4");
        wMap.put(ref1, "chaheObject1");
        wMap.put(ref2, "chaheObject2");
        map.put(ref3, "chaheObject3");
        map.put(ref4, "chaheObject4");
        System.out.println("String引用ref1,ref2,ref3,ref4 消失");

    }    public static void testWeakHashMap(){

        System.out.println("WeakHashMap GC之前");        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("WeakHashMap GC之后");        for (Object o : wMap.entrySet()) {
            System.out.println(o);
        }
    }    public static void testHashMap(){
        System.out.println("HashMap GC之前");        for (Object o : map.entrySet()) {
            System.out.println(o);
        }        try {
            System.gc();
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("HashMap GC之后");        for (Object o : map.entrySet()) {
            System.out.println(o);
        }
    }    public static void main(String[] args) {        init();        testWeakHashMap();        testHashMap();
    }
}/** 结果String引用ref1,ref2,ref3,ref4 消失WeakHashMap GC之前obejct2=chaheObject2obejct1=chaheObject1WeakHashMap GC之后HashMap GC之前obejct4=chaheObject4obejct3=chaheObject3Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket'HashMap GC之后obejct4=chaheObject4obejct3=chaheObject3**/

iOS培训,Swift培训,苹果开发培训,移动开发培训
上面代码和图示主演演示WeakHashMap如何自动释放缓存对象,当init函数执行完成后,局部变量字符串引用weakd1,weakd2,d1,d2都会消失,此时只有静态map中保存中对字符串对象的引用,可以看到,调用gc之后,hashmap的没有被回收,而WeakHashmap里面的缓存被回收了。

监听器和回调

内存泄漏第三个常见来源是监听器和其他回调,如果客户端在你实现的API中注册回调,却没有显示的取消,那么就会积聚。需要确保回调立即被当作垃圾回收的最佳方法是只保存他的若引用,例如将他们保存成为WeakHashMap中的键。


分类: Effective java


延伸阅读

向着Java前进-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训向着Java前进