美文网首页
[Effective Java] (06)消除过期的对象引用

[Effective Java] (06)消除过期的对象引用

作者: QyQiaoo | 来源:发表于2017-12-31 14:27 被阅读0次

虽然在Java语言中拥有垃圾收集(GC)回收程序,在创建对象后,不用程序员手动回收对象,但在某些情况下依然会造成内存泄漏的情况。

在支持垃圾回收的语言中,内存泄漏是很隐蔽,也可以称这类内存泄漏为“无意识的对象保留”更为恰当。如果一个对象引用被无意识地保留起来,那么来及回收机制不仅不会处理这个对象,而且也不会处理被这个对象所引用的所有其他对象,即使只有少量的几个对象引用被无意识地保留下来,也会有许许多多的对象呗排除在垃圾回收机制之外,从而对性能造成潜在的重大影响。

内存泄漏主要有如下三种方式:

1. 过期的对象引用引起的内存泄漏

示例代码如下:

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 obj) {
        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 tha capacity each time the array needs to grow.
     */
    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements,  2 * size + 1);
        }
    }
}

分析:如果一个栈先增长,然后在收缩,那么,从栈中弹出来的对象将不会被当作垃圾回收,即使使用栈的程序不在引用这些对象,它们也不会被回收,因为栈内部维护着对这些对象的过期引用(obsolete reference)。

过期引用:永远不会再被解除的引用,在上面代码中,凡是在elements数组的“活动部分(active portion)”之外的任何引用都是过期的。活动部分是指elements中下标小于size的那些元素。

解决办法:如下:一旦对象的引用已过期,清空这些引用。这样的一个好处是,如果它们以后又被错误的引用,程序就会立即抛出NullPointerException异常(尽快地检测出程序中的错误总是有益的)。

public Object pop() {
    if(size == 0) {
        throw new EmptyStackException();
    }
    Object res = elements[--size];
    elements[size] = null;    //Eliminate obsolete reference
    return res;
}

注:清空对象引用应该是一种例外,而不是一种规范行为。

  • 消除过期引用的最好的办法是让包含该引用的变量结束其生命周期,即为变量定义最紧凑的作用域。
  • 只要类是自己管理内存,程序员就应该警惕内存泄漏的问题。
2. 缓存中的对象引起的内存泄漏

内存泄漏的另一个常见的来源是缓存:对象应用存放在缓存中,当对象不再被使用时,很容易被遗忘掉而没有清理,于是,该对象引用会一直保存在缓存中,而你在逻辑上已经没有使用该对象,但该对象不会被GC回收,因为仍然有引用指向它。

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

  • 当所要的缓存项的生命周期是由该键的外部引用而不是由值决定时,可以采用WeakHashMap代表缓存;
    (代码示例,待续···)

  • 更为常见的情形是“缓存项的生命周期是否有意义”并不是可容易确定,随着时间的推移,其中的项会变得越来越没有价值,在这种情况下,缓存应该时不时的清楚掉没用的项,可采用后台进程(Timer或者ScheduledThreadPoolExecutor)来完成也可以在给缓存添加新条目的时候顺便进行清理(LinkedHashMap类中的removeEldestEntry方法容易实现该方案)对于更复杂的缓存,必须直接使用java.lang.ref
    (代码示例,待续···)

3. 监听器和其他回调引起的内存泄漏

内存泄漏的第三个常见来源是监听器和其他回调:我们实现了一个API,用户在这个API中注册了回调,却没有显示地曲线注册,除非采取某些方法,否则他们就会积聚,确保回调立即被当作垃圾回收的最佳方法就是保存它的弱引用(weak reference),如:只把它们保存成WeakHashMap中的键。
(代码示例,待续···)

相关文章

  • [Effective Java] (06)消除过期的对象引用

    虽然在Java语言中拥有垃圾收集(GC)回收程序,在创建对象后,不用程序员手动回收对象,但在某些情况下依然会造成内...

  • 消除过期的对象引用

    第6条:消除过期的对象引用 1. 为什么要消除过期的对象引用 java虽然有自己的垃圾回收机制,但是并没有那么的智...

  • Effective Java 第6条: 消除过期的对象引用

    示例:你找的出来下面代码中的内存泄漏(memory leak)吗 如果一个栈先增长,然后再收缩,那么pop出来的对...

  • 《Effective Java》第6条:消除过期的对象引用

    原文地址: https://itweknow.cn/detail?id=68 ,欢迎大家访问。 说到Java,大概...

  • 06-消除过期对象引用

    阅读原文 06-消除过期对象引用   01 - 当你从手工管理内存的语言转换到具有垃圾回收功能的语言的时候,程序员...

  • Effective Java 3rd 条目7 消除过期对象引用

    如果你是从手动内存管理的语言(比如C或者C++)切换到垃圾回收语言(比如Java),作为程序员你的工作会变得更容易...

  • 消除过期的对象引用

    你能看出以下代码哪里内存泄漏吗? 答案是: pop()方法存在内存泄漏。 内存泄漏可以称为“ 无意识的对象保持(u...

  • 消除过期的对象引用

    什么是过期的对象引用? 我们通过简单的栈实现来引入过期的对象引用。 实际上,这段程序中并没有很明显的错误。无论如何...

  • 消除过期的对象引用

    某些类内部维护着对象的过期引用(指永远也不会再被解除的引用),这些对象不会被当作垃圾回收,产生了内存泄露(这类内存...

  • 消除过期的引用对象

    C或者C++是手工管理内存语言,java语言是自带垃圾回收,程序员的工作就很容易,因为当你用完了对象之后,他会被自...

网友评论

      本文标题:[Effective Java] (06)消除过期的对象引用

      本文链接:https://www.haomeiwen.com/subject/pmyogxtx.html