java中可能的内存泄露

引子

一般情况下Java会自动回收内存,但是有些情况下还是会有可能发生内存泄露,比如下面这段代码

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);
        }
    }
}

这段代码看起来功能上没有什么问题,基本能通过每一项测试,然而却隐藏着一个问题,那就是可能会导致内存泄露。

哪里发生了内存泄露呢?

这个栈空间如果先增长,又缩减了。这段代码在出栈的时候,做了把size-1和对应位置元素返回,然后这时size-1位置(第size个元素)的元素引用仍然是存在的,导致在gc的时候并不会被回收。这是因为在栈内部维护者这些对象的过期引用,所谓的过期引用,是指永远不会再被解除的引用。其实在这里对象引用下标大于等于size的都是过期引用。

修复方法

这类问题的修复方法很简单,如下代码
public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        Object result = elements[--size];
        elements[size] = null;
        return result;
    }

总结

一般来说,只要类是自己管理内存,开发者就应该警惕内存泄露问题
内存泄露的另一个常见来源是缓存(主要是容器缓存)。放进缓存后很容易被遗忘,使得不再使用之后很长一段时间仍然在缓存中。这类解决方法一般可以参考下面几种:

  1. 使用WeakhashMap,不过这个要记住 当所要的缓存项的声明周期是由该键的外部引用而不是值决定时 WeakHashMap才有用

  2. 用一个后台线程来完成清除操作

  3. 给缓存添加新条目时顺便清理

内存泄露第三种常见来源是监听器和其他回调。比如你实现了个API,客户端在这个API中注册了回调,然而没有显式的取消注册,如果没有采取某些动作,它们就会不断的累计起来。这种解决方式可以通过只保存它们的弱引用,例如,只将它们保存成WeakHashMap的中键。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值