内存溢出和内存泄露的区别:内存泄露是内存溢出的一种
内存问题,说白了,就是不够用的问题。
所以,所有的内存问题,都是因为内存不够用导致的。
内存溢出,就是对象/数据不断地增多。而内存有限。所以,才会溢出嘛。
而内存泄露,其实,本质上,也是内存不够。只不过有一点细微的区别,就是内存泄露是因为对象没有释放。
这是概念。
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;
}