消除过期的对象引用

Java虽然有自己的垃圾回收机制,但是并没有那么的智能,对于被引用的对象,就算我们已经不在使用它了,但是Java的回收机制是不会回收它们的,人们称之为“内存泄漏”

以下为三种不同的内存泄漏场景,极其优化方案

1、只要类自己管理内存,就该警惕内存泄漏问题

例如Stack类自己管理内存,在元素出栈,忘记设置为null时,容易引起内存泄漏问题。

import java.util.Arrays;
import java.util.EmptyStackException;

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);
        }
    }
}
public class Demo {

    public static void main(String[] args) {
        Stack stack =new Stack();
        stack.push(Integer.valueOf(1));
        stack.push(Integer.valueOf(2));
        stack.push(Integer.valueOf(3));
        stack.push(Integer.valueOf(4));
        stack.push(Integer.valueOf(5));
        Integer numInteger = (Integer) stack.pop();
        System.out.println(numInteger);
    }
}

执行上述代码,从结果上面看好像也没啥问题。但其实先入栈5个元素,然后再出栈1个元素,那么此时出栈的这个元素是不会被销毁,因为elements中仍然持有指向该出栈对象的引用,会出现内存泄漏。因此我们可对上述代码进行如下优化:

import java.util.Arrays;
import java.util.EmptyStackException;

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();
        }
        Object result = elements[--size];
        elements[size]=null;
        return result;
    }

    /**
     * 扩容
     */
    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

2、内存泄漏的另一个常见来源是缓存

对象引用存放在缓存中,当对象不再被使用时,然后你忘记将该对象引用从缓存中清理掉。于是该对象引用会一直保存在缓存中,而你在逻辑上已经没有使用该对象,但是该对象不会被GC回收,因为仍然有引用指向它。 

解决办法:关键是要知道什么时候,缓存中的对象引用不再有用。在这个时候,就可以清理掉缓存中的对象引用。

一种例子是,如果实现的缓存是这样的:只要在缓存之外存在对某个项的键的引用,该项就有意义;如果没有存在对某个项的键的引用,该项就没有意义,那么我们可以使用WeakHashMap来代表缓存。 

import java.util.Map;
import java.util.WeakHashMap;

public class SocketManager {
    private Map<Socket,User> m = new WeakHashMap<Socket,User>();

    public void setUser(Socket s, User u) {
        m.put(s, u);
    }
    public User getUser(Socket s) {
        return m.get(s);
    }
}

WeakHashMap是一种弱引用的map,底层数据结构为数组+链表,内部的key存储为弱引用(除了自身有对key的引用,没有其它引用,也就是弱引用),在GC时如果key不存在强引用的情况下会被回收掉,而对于value的回收会在下一次操作map时回收掉,所以WeakHashMap适合缓存处理。

3、内存泄漏的第三个常见来源是监听器

在java编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXxxListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。

内存泄漏通常不会表现的很明显,可以在系统中存在很多年,只有通过检查代码,或借助Heap剖析工具才能发现内存泄漏问题,所以要尽量在内存泄漏发生之前就知道如何预测此类问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值