• 如何避免内存泄漏


    本来这一章应该是介绍与GC相关的内容,不过在此之前,我准备先和各位探讨一下一个编程的小技巧。当然,这个小技巧其实也是与GC密切相关的。

    不知道各位猿友有没看过一些JAVA内存相关的文章,里面在罗列建议的时候,经常会写出这样一条建议。

    第XX条、请在使用完对象之后,显示的将对象设置为null。

    原话不一定如此,但意思是一样的。话里所描述的意思,就是说我们以后写代码应该这么写。

    Object obj = new Object();  
    //to do something   
    obj = null;  

    这段代码有点C/C++的风格,obj=null代替了C/C++中的delete obj或者是free(obj),相当于在提示我们,就算有了GC,我们也要像没有GC一样,使用完对象就得将其赋为空值。

    首先,LZ这里要说明的是,将obj赋为空值,与C/C++中的delete其实有着天壤之别,LZ之所以说它们代替了delete和free,仅仅是因为它们出现在代码中的位置类似而已。

     obj=null只做了一件事,就是将obj这个引用变量与new Object()创造的实例的关联断开,实际上,实例所占用的内存空间依然没有释放。

    这里解释一下为何在有些时候不将对象赋为空值会造成内存泄露,我们考虑下面一段代码。

    import java.util.Arrays;
    
    public class Stack {
        
        private static final int INIT_SIZE = 10;
    
        private Object[] datas;
        
        private int size;
    
        public Stack() {
            super();
            datas = new Object[INIT_SIZE];
        }
        
        public void push(Object data){
            if (size == datas.length) {
                extend();
            }
            datas[size++] = data;
        }
        
        public Object pop(){
            if (size == 0) {
                throw new IndexOutOfBoundsException("size is zero");
            }
            return datas[--size];
        }
        
        private void extend(){
            datas = Arrays.copyOf(datas, 2 * size + 1);
        }
        
    }

      这段代码是一个简单的长度可伸缩的栈实现,在你写一段测试代码去测试它的时候,会发现它毫无问题。但是不好意思,这里面就有一个明显的“内存泄露”,这就是因为一些对象或者说引用没有设置为空值所造成的。       

              如果你还没看出来,LZ稍微提示一下,各位就会意识到了。这段代码里面,数组里的对象只会无限增长,而不会随着栈中元素的弹出而减少,减小的仅仅是对外展示的栈的大小size。考虑一个极端的场景,假设你曾经往栈中放入了100万个对象,最后使用了99万9千9百9十9个,从外部看来,栈中还只剩一个可用对象了,但是我们的栈依然持有着100万个对象的引用。如果JVM可以活过来的话,想必一定会把你蹂躏到死的。

              我们缺少的就是将对象赋为null值的那一步,所以pop方法应该改为下面的方式,而且各位可以去看一下ArrayList中remove方法的源码,也是类似的思路。

    /**
         * Removes the element at the specified position in this list.
         * Shifts any subsequent elements to the left (subtracts one from their
         * indices).
         *
         * @param index the index of the element to be removed
         * @return the element that was removed from the list
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }
     /*
         * Private remove method that skips bounds checking and does not
         * return the value removed.
         */
        private void fastRemove(int index) {
            modCount++;
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
        }

    这个内存泄露是非常隐蔽的,但是对于我们做集合类的删除时候就要充分给gc很好可以回收的代码,这样的代码就是优雅的代码。

  • 相关阅读:
    K8s系列【四、kubernetes核心组件工作流程及原理】
    K8s系列【五、Kubernetes实战演练】
    Linux系列【服务器安全篇】
    K8s系列【三、Kubernetes架构】
    Docker系列【离线安装指定版本的docker】
    K8s系列【配置Harbor私有仓库】
    Docker系列【docker: Error response from daemon: ...(iptables failed: iptables wait t nat A DOCKER p tcp d 0/0 dport 80 j DNAT】
    K8s系列【一、为什么要学习K8s?】
    K8s系列【二、K8s是什么?】
    CF1628AMeximum Array【二分】
  • 原文地址:https://www.cnblogs.com/jack-Star/p/8440345.html
Copyright © 2020-2023  润新知