在jdk1.2之前关于对象的引用其实只有一个定义:若reference类型的数据存储着另外一块内存的初始地址,那么这块内存就代表着一个引用。对应的有关引用的状态就是引用和被引用。其实在后来语言的发展过程中,发现对于一些缓存类型的对象若是内存够用则存在,若是不够则清除。那么如何描述这种类型的对象呢?于是便出现了多种类型的引用类型。
1、java你知道的引用类型?
强引用:a = new A();只要引用关系存在永远不会被gc
软引用:SoftReference sr = new SoftReference<>(new T()); 这样创建的对象在堆内存不够的时候,进行垃圾回收的时候会被清除掉。
弱引用:WeakReference wr = new WeakReference<>(new T());这样创建的对象在每一次gc时候都会被回收掉。
虚引用:PhantomReference pr = new PhantomReference<>(new T(),Queue) 唯一作用是在对象被回收之后会受到一个系统通知。jdk中直接内存的回收就用到虚引用,由于jvm自动内存管理的范围是堆内存,而直接内存是在堆内存之外(其实是内存映射文件,自行去理解虚拟内存空间的相关概念),所以直接内存的分配和回收都是有Unsafe类去操作,java在申请一块直接内存之后,会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。
3、ThreadLocal为什么容易发生内存泄漏,怎么避免?
threadlocal是如何做到将变量作为线程变量呢?其实在tl.set(value)时,并不是将变量放在ThreadLocal中,而是通过获取当前线程,再获取当前线程的ThreadLocalMap 来设置,key为 ThreadLocal tl,value为 T value。本质上还是通过一个Entry设置。 由于 tl是 通过弱应用放在Entry中的,所以在gc时可以及时收走ThreadLocal类型的对象,而收集之后Entry中的key值自然为null,那么value此时是不是就是一直使用的内存了呢?无法被回收?其实这正是replaceStaleEntry
存在的意义、将为null值得entry
及时利用,防止内存泄漏。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}