第六条:消除过期的对象引用

即使是使用具有垃圾回收功能的语言,我们也需要考虑内存管理的事情。

例如:

/**
 * @description: 消除过期的对象引用
 * @author: lty
 * @create: 2021-05-10 15:30
 **/
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;
    }

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

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

这段代码没有明显的错误,但是却隐藏着一个问题,“内存泄露”。

这段代码哪里存在内存泄露呢?
如果一个栈先是增长,然后再收缩,那么,从栈中弹出来的对象将不会被当作垃圾回收,即使使用栈的程序将不再引用这些对象,它们也不会被回收。因为栈内部维护着对这些对象的过期引用(指永远也不会解除的引用)。本例中凡是在elements数组的“活动部分(下标小于size的那些元素)”之外的任何引用都是过期的。

如果一个对象的引用被无意识地保留起来了,那么垃圾回收机制不仅不会处理这个对象,而且也不会处理这个对象所引用的所有其他对象。
解决方法:一旦对象引用已经过期,只需清空这些引用即可。

本例修改:
在这里插入图片描述
清空过期引用的另一个好处是,如果它们以后又被错误地解除引用,程序就会立刻抛出空指针异常,而不是悄悄地错误的运行下去。

清空对象引用应该是一种特例,而不是一种规范行为。不是对于每个对象引用一旦程序不再用它,就把它清空。

那什么时候应该清空引用呢?
一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。本例中的Stack类就是自己管理内存的。存储池包含了elements数组(对象引用单元,而不是对象本身)的元素,数组“活动部分(下标小于size的那些元素)”中的元素是已经分配的,而其他部分的元素则是自由的。但是对于垃圾回收器来说所有对象引用都是一样有效的。一旦数组元素变成了非活动部分的一部分,程序员就手工清空这些数组元素。

内存泄露的三种来源及解决方法:

第一种:类是自己管理内存
一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。

解决方法:一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。

第二种:缓存
一旦你把对象引用放到缓存中,它很容易被遗忘掉,从而使得它在没有用之后很长一段时间仍然保存在缓存中。

解决方法:
(1)当所要的缓存项的生命周期是由该键的外部引用而不是由值决定时,也就是说在缓存之外存在对某个项的键的引用,改项才有意义,就可以使用WeakHashMap代表缓存,当缓存中的项过期之后,它们就会自动被删除。
(2)常见的情形则是,“缓存项的生命周期是否有意义”并不是很容易确定,随着时间的推移,其中的项会变得越来越没有价值。在这种情况下,缓存应该时不时地清除掉没用的项。这项清除工作可以由一个后台线程(可能是Timer或者ScheduledThreadPoolExecutor)来完成,或者也可以在给缓存添加新项的时候顺便进行清理。LinkedHashMap类利用它的removeEldestEntry方法可以很容易地实现后一种方案。对于更加复杂的缓存,必须直接使用java.lang.ref。

第三种:监听器和其他回调
比如你实现了一个API,客户端在这个API中注册回调,却没有显示地取消注册,那么除非你采取某些动作,否则他们就会积聚。

解决方法:确保回调立即被当做垃圾回收的最佳方法是只保存他们的弱引用(weak reference),例如,只将它们保存成WeakHashMap中的键。

由于内存泄漏通常不会表现出明显的失败迹象,所以他们可以在一个系统中存在很多年。往往只有通过仔细检查代码,或者借助于Heap剖析工具(Heap Profiler)才能发现内存泄漏问题。因此,如果能在内存泄漏发生之前就知道如何预测此类问题,并阻止它们发生,那是最好不过的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值