内存泄露-栈

217 阅读3分钟

理论知识

一个类只要是自己管理内存,就很容易发生内存泄漏,比如栈,队列,都是封装了数组。

而实际使用的时候,即对象元素进出栈的时候,数组的大小只被使用了一部分。也就是说,一般情况下,数组有一部分是没有被使用的。

但是呢,在元素刚刚被删除之前又是被使用的,现在呢,被删除了,空出来了一个位置/引用,这个位置指向了一个对象。

注意这里的点,该元素被删除,只是相对于栈这个数据结构而言的。但是对数组本身来说,那个位置仍然还是指向了之前指向的那个对象。

而下次使用的时候,即入栈的时候,这个位置又指向了一个新的对象。那么,这个时候,旧的对象不就没有引用指向它了吗?不就可以自动被jvm垃圾回收了吗?

那就不存在内存泄露了啊。

所以,就算没有显式的设置旧对象引用为null,也只不过是浪费了数组没有被使用的那一部分内存空间的引用指向的对象。

但是,如果数组很大,而且有很多这样的数组,那么就真的可能存在内存泄漏了。

解决方法?出栈的时候,显式设置对象引用为null,作用是让jvm立即回收对象,而不是等到入栈的时候,由于引用指向了新的对象,旧对象才没有引用指向它。

示意图

image.png 虽然栈少了一个数据,但是那个出栈的引用(即c的内存位置)仍然指向对象本身C,由于仍然有引用指向C,导致C不能被立即回收,所以需要显式设置为null。


看jdk8源码

Stack.java

public
class Stack<E> extends Vector<E> {

/**
     * Removes the object at the top of this stack and returns that
     * object as the value of this function.
     *
     * @return  The object at the top of this stack (the last item
     *          of the <tt>Vector</tt> object).
     * @throws  EmptyStackException  if this stack is empty.
     */
    public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1); //出栈一个元素

        return obj;
    }

Vector.java

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

/**
     * Deletes the component at the specified index. Each component in
     * this vector with an index greater or equal to the specified
     * {@code index} is shifted downward to have an index one
     * smaller than the value it had previously. The size of this vector
     * is decreased by {@code 1}.
     *
     * <p>The index must be a value greater than or equal to {@code 0}
     * and less than the current size of the vector.
     *
     * <p>This method is identical in functionality to the {@link #remove(int)}
     * method (which is part of the {@link List} interface).  Note that the
     * {@code remove} method returns the old value that was stored at the
     * specified position.
     *
     * @param      index   the index of the object to remove
     * @throws ArrayIndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index >= size()})
     */
    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 */  //每次出栈一个元素的时候,显式设置为null,让jvm立即回收
    }

image.png

参考

effective java