内存泄露

99 阅读2分钟

内存溢出和内存泄露的区别:内存泄露是内存溢出的一种

内存问题,说白了,就是不够用的问题。

所以,所有的内存问题,都是因为内存不够用导致的。

内存溢出,就是对象/数据不断地增多。而内存有限。所以,才会溢出嘛。

而内存泄露,其实,本质上,也是内存不够。只不过有一点细微的区别,就是内存泄露是因为对象没有释放。

这是概念。

c c++

需要自己申请和释放内存,但是没有释放,所以这个时候,就是内存泄露,因为不能被回收。

java

基本上没有内存泄露的问题,或者说很少。因为不能root引用,就会被回收。

ThreadLocal

会不会存在内存泄露的问题?会。

为什么会?因为可能会存在key被回收,但是value没有被回收。因为map是根据key找到value的,key都没了,value自然不能被回收。

为什么会存在key被回收,但是value没有被回收的情况?因为key继承了弱引用,所以垃圾回收的时候,key会被回收。但是value不会被回收,因为找不到key,key已经没了。

怎么解决?remove()。就是每次请求完成,在业务逻辑的最后,即finally里,删除当前线程的数据。本质是置为value=null。 其实,每次get set的时候,也会删除所有key=null的数据,即设置value=null。remove也是一样。具体来说就是for循环校验判断是否为null,详细可以看源码。

源码

ThreadLocal.remove()源码

/**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
/**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) { //遍历数据
                if (e.get() == key) {
                    e.clear(); //
                    expungeStaleEntry(i); //置空
                    return;
                }
            }
        }
/**
         * Expunge a stale entry by rehashing any possibly colliding entries
         * lying between staleSlot and the next null slot.  This also expunges
         * any other stale entries encountered before the trailing null.  See
         * Knuth, Section 6.4
         *
         * @param staleSlot index of slot known to have null key
         * @return the index of the next null slot after staleSlot
         * (all between staleSlot and this slot will have been checked
         * for expunging).
         */
        private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null; //置空value=null
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

参考

wuwenliang.net/2018/10/19/…