《Effective Java》读书笔记05--过期的对象引用

一、Java的垃圾回收

大学时学过C和C++,后来学JAVA,感觉最爽的就是System.gc(),哈哈,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];
    }

    /**
     * Ensure space for at least one more element, roughly
     * doubling the capacity each time the array needs to grow.
     */
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}
这段程序功能上没多大问题,就是存在一个"内存泄露",这个比C和C++的要隐蔽的多。随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序性能的降低会逐渐表现出来。在极端的情况下,这种内存泄露会导致磁盘交换(Disk Paging),甚至导致程序失败,OutOfMemoryError错误,这个我很常见的,没办法,内存泄露的机子都卡死了,呵呵。。。

问题分析:内存泄露发生在哪里?

如果一个栈先增长,然后收缩,那么,从栈中弹出来的对象将不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。因为,栈内部维护着这些对象的过期引用(obsolete reference)。过期引用,是指永远也不会再被解除的引用。此处,凡是下标大于size的引用都是过期引用,没有办法再获取到该引用的。

pop方法修正版:

 public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
		Object result = elements[--size];
		elements[size] = null;  //清空过期引用
        return result;
    }

二、内存泄露多发区

1、自己管理内存

Stack类为什么会出现内存泄露?因为,它自己管理内存!存储池包含了elements数组的元素(当然是引用了),数组活动区域(下标小于size)是已分配的,即这些引用是有用的,非活动区域(下标大于size)是自由的,即对于我们来说是无所谓的,最好是清空的,省的占地方。但对于垃圾回收期而言,elements数组中的所有对象引用都是同等有效的,它才不知道你想使用那部分呢!程序员可以告诉垃圾回收器,我对非活动区域不敢兴趣,你给我回收了吧,做法就是:一旦数组元素变成非活动部分的一部分,程序员就手动清空这些数组元素。

一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。

2、缓存

WeakHashMap:适用于只要在缓存之外存在对某个项的键的引用,该项就有意义的情况。当缓存中的项过期之后,它们就会自动被删除。

备注:只有当所要的缓存项的生命周期是有该键的外部引用而不是值决定时,weakHashMap才有用处。HashMap貌似是值吧!

LinkedHashMap:可以利用removeEldestEntry方法在往缓存中添加新条目时清理过期没用的项。

更加复杂的缓存可以直接使用java.lang.ref。

3、监听器和回调

客户端调用API时注册回调,却没显式地取消注册,这样回调就会积聚,除非API提供者会自动清理。

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

三、最佳编程实践

1、一旦对象引用过期,最好立即清空引用,除非你能确保包含该引用的对象会结束生命周期。

2、只要类自己管理内存,就要警惕内存泄露问题。

3、对于回调来说,只保存他们的弱引用,比如,将它们保存成WeakHashMap中的键。

4、使用分析工具分析是否存在内存泄露,比如Heap剖析工具(Heap Profiler)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值