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

Java的垃圾回收机制并不代表我们不需要考虑内存管理的问题。

考虑:

public class Stack {
    pprivate Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITAL_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);
    }
}

这是自己编写的一个栈。

这段程序没有任何明显的错误,但这个程序中隐藏着一个问题,内存泄漏。

如果一个栈先是增长,然后收缩,那么从栈中弹出来的对象不会被当作垃圾回收,这是因为栈内部仍然维护着这些过期对象的引用,所谓过期引用是指elements中下标大于等于size的那些元素(由于栈会增长收缩,所以这是完全有可能的),如果一个对象被无意识地(即我们不希望地)保留下来,那么这个引用所引用的其他对象也不会被垃圾回收机制回收,因为仍然存在着引用,GC认为这些对象仍然是会被使用的。随着程序的运行时间增长,过期的对象引用占用的内存会越来越大,导致程序性能下降。

这类问题的解决方法是:一旦对象引用已经过期,只需清空这些引用即可。

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

清空对象引用应该是一种例外,而不是一种规范行为,不要求也不建议程序员对于每个对象引用,一旦程序不再用,就把它清空,这通常会把代码弄的很混乱。

消除过期引用的最好办法是在最紧凑的作用域范围内定义每一个变量,当作用域被执行完,GC会自动把过期作用域的变量回收掉。

什么时候应该自己清空引用?

一般而言,只要是自己管理内存,就应该警惕内存泄漏问题。假如你开辟了一段内存空间,并一直持有这段空间的引用,就有责任管理它,因为GC无法自动完成对你承诺管理的内存的回收,除非你告诉它(显式地清空引用)。

在JDK中,已经有现成的Stack类供我们使用,来看看它是怎么实现pop的:

public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();//这个方法将栈顶的元素取出来,但并不会把栈顶元素弹出
        removeElementAt(len - 1);//这是Stack父类的方法,将栈顶元素弹出

        return obj;
}

public synchronized void removeElementAt(int index) {
        modCount++;
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work *//可以看到,jdk的实现就是显式地把引用清空,以此告诉GC将过期引用回收
}

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

时间: 2024-10-30 22:49:32

第六条:消除过期的对象引用的相关文章

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

看下面的关于栈的程序: package com.duo.month10day25; import java.util.Arrays; import java.util.EmptyStackException; public class StackTest { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public StackTes

消除过期的对象引用

本文涉及的概念 1.Java的内存泄露 2.出现Java内存泄露的几种常见场景 Java的内存泄露 Java语言,创建对象后,程序员不用手动回收对象.Java虚拟机会手动回收不使用的对象(没有引用指向该对象).那么,为什么还出现泄露.Java的内存泄露是指这样一种情况,创建一个对象,系统中依然存在引用指向该对象,该对象是可达的:但是,我们找不到该引用,或者我们没发现该引用的存在.于是,一直存在该对象,但是,它对我们不可用,Java虚拟机也不能回收该对象. “ 在Java中,内存泄漏就是存在一些被

Effective Java 之-----消除过期的对象引用

public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_CAPACITY]; } public void push(Object e){ ensureCapacity(); elements[size++] = e; } pu

第二章:创建和销毁对象。ITEM6:消除过期的对象引用。

1 package com.twoslow.cha2; 2 3 import java.util.Arrays; 4 import java.util.EmptyStackException; 5 6 public class Stack { 7 8 private Object[] elements ; 9 10 private int size = 0; 11 12 private static final int DEFAULT_INITIAL_CAPACITY = 16 ; 13 14

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

让咱们先来看一下数组实现栈的例子: package chaper1; import java.util.Arrays; import java.util.EmptyStackException; public class Stack_Test00 { private Object[] elements; private static int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack

亚马逊贝索斯的六条管理哲学

晚上无意看了贝索斯的一篇文章,里面讲到他的管理哲学,感觉还不错,虽然很多是有点正确的废话,但里面还有不少值得学习的.最重要的是他的长远规划和目标签到习惯. 一间车库内创建,员工最初只有几个人,但如今它已彻底改变了我们购买从图书.玩具到服装等一切商品的方式.亚马逊目前是美国百强企业之一,它的成功主要归功于像Amazon Kindle这样经过长期规划的产品. 贝佐斯2011年在接受<连线>杂志采访时说:“如果你所做的每件事都围绕三年规划展开,那么你的竞争对手就太多了;但如果你愿意投资一个七年期的规

Effective Objective-C 2.0 — 第二章 对象、消息、运行期 - 第六条:理解“属性”这一概念

开发者通过对象来 存储并传递数据. 在对象之间传递数据并执行任务的过程就叫做“消息传递”. 这两条特性的工作原理? Objective-C运行期环境(Objective-C runtime) ,提供了使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑. 第六条:理解“属性”这一概念 property:

[Effective Java 读书笔记] 第三章类和接口 第十六条

第十六条 复合优先于继承 如果不确定B和A的关系是,is-a的关系,B确实也是A,那么久不应该使用B继承A,否则会暴露实现细节, 你的实现都会限制在原始的实现上. 书中举的第一个例子,实现了一个类extends HashSet类,因为缺少对HashSet类的addAll方法的理解(addAll会重复调用add方法),导致多统计了一倍的调用次数,这就是自己的实现限制在父类的实现上. 同时书中还有一个说明,如果超类在后续的版本中增加了一个新的方法, 正好和你的类里新增的方法签名一致(参数列表和函数名

给360的六条建议(禁止异地登录,普通用户500G足够用了)

个人觉得,360云盘哪怕做个小改进,都不至于走到现在的地步,最后六条建议! 1.弄个实名制,身份证和手机号码双重绑定,每人限制申请一个账户. 2.禁止云盘的一切分享功能,采用封闭式,个人云盘资料其他人将无访问权限. 3.PC端云盘账号无法异地登陆,360云盘绑定的手机号码可以异地登陆. 4.云盘免费用户最大容量500G(PS:正常使用500G完全够,其次会员会多些) 5.云盘普通会员最大容量1T,高级会员最大容量2T,价格360公司自定. 6.按照以上5条适当调整执行,360云盘必将永垂不朽!